012. 템플릿(Template)
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. 템플릿의 원리 이해
- 여기는 책을 읽어보면 될듯 하다.
이 글은 스프링노트에서 작성되었습니다.
'2. C/C++ > 02. C++' 카테고리의 다른 글
013. 예외처리(Exception Hangling) (0) | 2008.12.07 |
---|---|
011. string 클래스 디자인 (0) | 2008.12.07 |
010. 연산자 오버로딩 (0) | 2008.12.07 |
009. virtual의 원리와 다중 상속 (0) | 2008.12.07 |
008. 상속과 다형성 (0) | 2008.12.07 |