jam 블로그

[C++] 012. 템플릿(Template) 본문

개발 및 관련 자료/C

[C++] 012. 템플릿(Template)

kid1412 2013. 5. 12. 20:18
728x90

I. 템플릿에 대한 이해

  • 간단히 말해서 템플릿을 쓰는 이유는

    •  함수의 리턴 값이나 받는 인자값에 대한 자료형을 정해주지 않는 다는 것이다.

#include <iostream>
using namespace std;

template<typename T>
T add(T a,T b)
{
 return a+b;
}

int main()
{
 cout<<add(10,20)<<endl;
 cout<<add(1.1,2.1)<<endl;
 cout<<add(10,1.1)<<endl;
 return 0;
}

  • 위에 소스에서 템플릿을 선언 할때는

    • template<typename T> T 함수명(인자값) 이런식으로 나가면 된다.

      • 참고로 template<typename T>와 template<class T>가 같은 것이다.(대부분 전자를 많이 쓰는 거 같다.)
    • cout 3줄중 위에 2줄은 같은 형 즉 int형이면 int형만 double이면 double형만 써서 잘 되지만

      • 그 아래 줄에는 int형과 double형을 같이 넣으면 T에 대한 모호성 때문에 오류가 난다.

 

II. 함수 템플릿

  • 함수 템플릿의 정의

    • 템플릿 함수가 아닌 함수 템플릿이라고 부르는 것이 맞는 표현이라고 한다.

 

  • 둘 이상의 타입에 대해서 템플릿화하기

#include <iostream>

using namespace std;

template<typename T>
void ShowData(T a,T b)
{
 cout<<a<<endl;
 cout<<b<<endl;
}

int main()
{
 ShowData(1,2);
 ShowData(3,2.5);
 return 0;
}

  • 앞전에서 와 같이 메인에서 오류가 난다.

    • 둘이상의 형을 같이 받을수 없기 때문이다. 어찌해야 할까?

 #include <iostream>

using namespace std;

template<typename T1,typename T2>
void ShowData(T1 a,T2 b)
{
 cout<<a<<endl;
 cout<<b<<endl;
}

int main()
{
 ShowData(1,2);
 ShowData(3,2.5);
 return 0;
}

  • 위와 같은 식으로 자료형이 두가지 이상 결정될 수 있다.

 

  • 함수 템플릿의 특수화(template specialization)

 #include <iostream>

using namespace std;

template<typename T>
int Sizeof(T a)
{
 return sizeof(a);
}

int main()
{
 int i = 10;
 double e = 7.7;
 char* str = "Goodday Commander";
 cout<<Sizeof(i)<<endl;
 cout<<Sizeof(e)<<endl;
 cout<<Sizeof(str)<<endl;

 return 0;
}

  •  위의 소스는 전달된 인자를 참조해서 차지하는 메모리 크기를 바이트 단위로 리턴하는 소스이다.

    • 만약 전달되는 인자가 문자열을 가리키는 포인터일 경우 문자열의 길이를 리턴해 주기를 원한다면 아래와 같이 하면 된다.

 #include <iostream>

using namespace std;

template<typename T>
int Sizeof(T a)
{
 return sizeof(a);
}
template <>
int Sizeof(char* a)
{
 return strlen(a);
}

int main()
{
 int i = 10;
 double e = 7.7;
 char* str = "Goodday Commander";
 cout<<Sizeof(i)<<endl;
 cout<<Sizeof(e)<<endl;
 cout<<Sizeof(str)<<endl;

 return 0;
}

  • 위의 소스를 보면 template<> 이렇게 선언했는데 특수화를 선언한 것이다.

    • 즉 template<typename T> 에서 예외로 따로 사용할꺼 있다면 특수화를 쓰면 된다.

 

III. 클래스 템플릿

  • 클래스 템플릿의 정의

#include <iostream>

using namespace std;

class Data
{
 int data;
public:
 Data(int d)
 {
  data = d;
 }
 void SetData(int d)
 {
  data=d;
 }
 int GetData()
 {
  return data;
 }
};

int main()
{
 Data d1(0);
 d1.SetData(10);
 Data d2(100);

 cout<<d1.GetData()<<endl;
 cout<<d2.GetData()<<endl;
 return 0;
}

  •  위의 소스는 int형 데이터 하나를 저장할 수 있는 Data 클래스 이다.

    • 위의 소스를 template형식으로 바꾸어 보자

 

#include <iostream>

using namespace std;

template <typename T>
class Data
{
 T data;
public:
 Data(T d)
 {
  data = d;
 }
 void SetData(T d)
 {
  data=d;
 }
 T GetData()
 {
  return data;
 }
};

int main()
{
 Data<int> d1(0);
 d1.SetData(10);
 Data<char> d2('a');

 cout<<d1.GetData()<<endl;
 cout<<d2.GetData()<<endl;
 return 0;
}

  •   클래스에 템플릿 하는 방법은 위와 같이 하면 된다.

    • 메인에서 d1을 int로 생성하겠다는 의미이다.
    • d2는 char로 생성하겼다는 의미이다.

 

  •  클래스 템플릿의 선언과 정의 분리

 #include <iostream>

using namespace std;

template<typename T>
class Data
{
 T data;
public:
 Data(T d);
 void SetData(T d);
 T GetData();
};

template<typename T>
Data<T>::Data(T d)
{
 data = d;
}

template<typename T>
void Data<T>::SetData(T d)
{
 data = d;
}

template<typename T>
T Data<T>::GetData()
{
 return data;
}

  • 위의 소스를 보면 Data<T>::SetData() 이런식으로 되어있는데 Data::SetData()라고 안한 이유

    • Data라고 하였다면 그냥 클래스의 멤버 함수를 정의한것이다. Data<T>라고 하였다면 클래스 템플릿을 선언한 것이다.
    • 그리고 template<typename T>를 선언을 또 해주었냐면 T가 무엇인지 보충 설명해 주는 역할을 하기 때문이다.

 

  • 스택 클래스의 템플릿화

    • 스택 : LIFO(Last Input, First Out)특징을 지니는 자료구조이다.
    • push : 데이터를 저장하는 행위
    • pop : 쌓아 올려진 데이터를 꺼내는 행위

#include <iostream>

using namespace std;

class Stack
{
private:
 int topIdx;
 char* stackPtr;
public:
 Stack(int s =10);
 ~Stack();
 void Push(const char& pushValue);
 char Pop();
};

Stack::Stack(int len)
{
 topIdx = -1;
 stackPtr = new char[len];
}
Stack::~Stack()
{
 delete[] stackPtr;
}

void Stack::Push(const char &pushValue)
{
 stackPtr[++topIdx] = pushValue;
}

char Stack::Pop()
{
 return stackPtr[topIdx--];
}


int main()
{
 Stack stack(10);
 stack.Push('A');
 stack.Push('B');
 stack.Push('C');

 for (int i=0;i<3;i++)
 {
  cout<<stack.Pop()<<endl;
 }
 return 0;
}

  •  위의 소스는 Stack의 역할을 보여주는 것이다.

    • 이제 이것을 템플릿화 해보겠다.

 #include <iostream>

using namespace std;

template <typename T>
class Stack
{
private:
 int topIdx;
 T* stackPtr;
public:
 Stack(int s =10);
 ~Stack();
 void Push(const T& pushValue);
 T Pop();
};

template<typename T>
Stack<T>::Stack(int len)
{
 topIdx = -1;
 stackPtr = new T[len];
}
template<typename T>
Stack<T>::~Stack()
{
 delete[] stackPtr;
}

template<typename T>
void Stack<T>::Push(const T& pushValue)
{
 stackPtr[++topIdx] = pushValue;
}
template<typename T>
T Stack<T>::Pop()
{
 return stackPtr[topIdx--];
}


int main()
{
 Stack<char> stack1(10);
 stack1.Push('A');
 stack1.Push('B');
 stack1.Push('C');

 for (int i=0;i<3;i++)
 {
  cout<<stack1.Pop()<<endl;
 }

 Stack<int> stack2(10);
 stack2.Push(10);
 stack2.Push(20);
 stack2.Push(30);

 for (int i=0;i<3;i++)
 {
  cout<<stack2.Pop()<<endl;
 }
 return 0;
}

  • 위와 같이 자주 사용하는 자료구조와 알고리즘을 템플릿으로 정의해 놓으면 좋다.

    • STL이라는 템플릿 모음이 있다.

 

IV. 템플릿의 원리 이해

  • 여기는 책을 읽어보면 될듯 하다.
Comments