[열혈 C++] Chap14: 템플릿2
1. 템플릿 확장
✏️클래스 템플릿, 배열 클래스 템플릿
-
배열 클래스에 저장하고 싶은 자료형이 클래스 템플릿이어도 가능하다.
-
BoundCheckArray<Point<int>> arr(50);
이런식으로 만들 수 있다. -
BoundCheckArray<Point<int>*> arr(50);
이것도 된다. -
typedef Point<int>* POINT_PTR;
로 해놓고BoundCheckArray<POINT_PTR> arr(50);
이렇게 해도 된다.
-
✏️템플릿 클래스 인자로 받는 함수, friend
-
예제
#include <iostream> using namespace std; template <typename T> class Point { private: T xpos, ypos; public: Point(T x=0, T y=0): xpos(x), ypos(y) { } void ShowPosition() const { cout<<'['<<xpos<<", "<<ypos<<']'<<endl; } friend Point<int> operator+(const Point<int>&, const Point<int>&); // 클래스 템플릿도 일반함수에 friend선언이 가능하다 friend ostream& operator<<(ostream& os, const Point<int>& pos); }; Point<int> operator+(const Point<int>& pos1, const Point<int>& pos2) // +연산자 오버로딩 { // Point<int>는 템플릿 클래스에 자료형이 선언되었으므로 이 객체 둘을 인자로 전달받는 일반 함수이다 return Point<int>(pos1.xpos+pos2.xpos, pos1.ypos+pos2.ypos); } ostream& operator<<(ostream& os, const Point<int>& pos) // <<연산자 오버로딩 { os<<'['<<pos.xpos<<", "<<pos.ypos<<']'<<endl; return os; } int main(void) { Point<int> pos1(2, 4); //[2,4] Point<int> pos2(4, 8); //[4,8] Point<int> pos3=pos1+pos2; //[6,12] cout<<pos1<<pos2<<pos3; return 0; }
2. 클래스 템플릿 특수화
-
이유 : 특정 자료형을 기반으로 생성된 객체에 대해, 구분이 되는 다른 기능, 행동을 적용하기 위해서이다.
-
템플릿을 구성하는 멤버함수, 변수를 다르게 행동하도록 정의할 수 있다.
#include <iostream>
#include <cstring>
using namespace std;
template <typename T>
class Point
{
private:
T xpos, ypos;
public:
Point(T x=0, T y=0): xpos(x), ypos(y)
{ }
void ShowPosition() const
{
cout<<'['<<xpos<<", "<<ypos<<']'<<endl;
}
};
template <typename T>
class SimpleDataWrapper
{
private:
T mdata;
public:
SimpleDataWrapper(T data) : mdata(data)
{ }
void ShowDataInfo(void)
{
cout<<"Data: "<<mdata<<endl;
}
};
template<>
class SimpleDataWrapper <char*>
{
private:
char* mdata;
public:
SimpleDataWrapper(char* data)
{
mdata=new char[strlen(data)+1];
strcpy(mdata, data);
}
void ShowDataInfo(void)
{
cout<<"String: "<<mdata<<endl;
cout<<"Length: "<<strlen(mdata)<<endl;
}
~SimpleDataWrapper()
{
delete []mdata;
}
};
template<>
class SimpleDataWrapper <Point<int>>
{
private:
Point<int> mdata;
public:
SimpleDataWrapper(int x, int y) : mdata(x, y)
{ }
void ShowDataInfo(void)
{
mdata.ShowPosition();
}
};
int main(void)
{
SimpleDataWrapper<int> iwrap(170); //일반 클래스 템플릿이 적용되어 만들어진 템플릿 클래스의 객체
iwrap.ShowDataInfo(); // Data: 170
SimpleDataWrapper<char*> swrap("Class Template Specialization"); //특수화된 클래스 템플릿이 적용되어 만들어진 템플릿 클래스 객체
swrap.ShowDataInfo(); //String: Class Template Specialization
//Length: 29
SimpleDataWrapper<Point<int>> poswrap(3, 7); //특수화된 클래스 템플릿이 적용되어 만들어진 템플릿 클래스 객체
poswrap.ShowDataInfo(); //[3, 7]
return 0;
}
✏️부분 특수화
- 부분적으로만 특수화 시킬 수 있다.
#include <iostream>
using namespace std;
template <typename T1, typename T2> //템플릿 매개변수 선언
class MySimple
{
public:
void WhoAreYou()
{
cout<<"size of T1: "<<sizeof(T1)<<endl;
cout<<"size of T2: "<<sizeof(T2)<<endl;
cout<<"<typename T1, typename T2>"<<endl;
}
};
template<>
class MySimple<int, double>
{
public:
void WhoAreYou()
{
cout<<"size of int: "<<sizeof(int)<<endl;
cout<<"size of double: "<<sizeof(double)<<endl;
cout<<"<int, double>"<<endl;
}
};
template<typename T1> //여기서 double만 특수화 되었다.
class MySimple<T1, double>
{
public:
void WhoAreYou()
{
cout<<"size of T1: "<<sizeof(T1)<<endl;
cout<<"size of double: "<<sizeof(double)<<endl;
cout<<"<T1, double>"<<endl;
}
};
int main(void)
{
MySimple<char, double> obj1;
obj1.WhoAreYou();
MySimple<int, long> obj2;
obj2.WhoAreYou();
MySimple<int, double> obj3;
obj3.WhoAreYou();
return 0;
}
- 전체 특수화가 부분 특수화보다 우선시 된다.
- 즉,
<int, double>
템플릿이 있고,<T , double>
템플릿이 있을 때, <int, double>은 전체 특수화인<int,double>
템플릿 클래스의 객체가 된다.
3. 템플릿 인자
-
결정되지 않은 자료형을 의미하는
T, T1, T2
같은 애들을 템플릿 매개변수라고 한다. -
템플릿 매개변수에는 변수 선언이 올 수 있다.
#include <iostream>
using namespace std;
template <typename T, int len> //템플릿 매개변수 선언에 변수의 선언이 명시됨
class SimpleArray
{
private:
T arr[len];
public:
T& operator[] (int idx)
{
return arr[idx];
}
T& operator=(const T&ref)
{
for(int i=0; i<len; i++)
arr[i]=ref.arr[i];
}
};
int main(void)
{
SimpleArray<int, 5> i5arr1;
for(int i=0; i<5; i++)
i5arr1[i]=i*10;
SimpleArray<int, 5> i5arr2;
i5arr2=i5arr1;
for(int i=0; i<5; i++)
cout<<i5arr2[i]<<", ";
cout<<endl;
SimpleArray<int, 7> i7arr1;
for(int i=0; i<7; i++)
i7arr1[i]=i*10;
SimpleArray<int, 7> i7arr2;
i7arr2=i7arr1;
for(int i=0; i<7; i++)
cout<<i7arr2[i]<<", ";
cout<<endl;
return 0;
}
- <int, 5>와 <int, 7>에 대한 각각의 템플릿 클래스가 별도로 만들어진다
-
생성자로 넣어주면 되는데 이렇게 작성한 코드가 같는 의미는, 자료형과 길이가 같은 배열 객체만 대입과 복사가 허용되기 때문에 길이가 다른 두 배열 객체간의 대입 및 복사에 대한 부분을 신경 쓸 필요가 없다는 것이다.
- 템플릿 매개변수에 디폴트 값 지정도 가능하다
<typename T, int len = 7> //이런거 됨 class SimpleArray { private: T arr[len]; public: T& operator[] (int idx) { return arr[idx]; } T& operator=(const T&ref) { for(int i=0; i<len; i++) arr[i]=ref.arr[i]; } };
4. 템플릿과 static
✏️함수 템플릿과 static
- 함수 템플릿 내 선언된 static 변수는 템플릿 함수 별로 각각 존재하게 된다.
-
예제
#include <iostream> using namespace std; template <typename T> void ShowStaticValue(void) { static T num=0; num+=1; cout<<num<<" "; } int main(void) { ShowStaticValue<int>(); // 1 ShowStaticValue<int>(); // 2 ShowStaticValue<int>(); // 3 cout<<endl; ShowStaticValue<long>(); // 1 ShowStaticValue<long>(); // 2 ShowStaticValue<long>(); // 3 cout<<endl; ShowStaticValue<double>(); // 1 ShowStaticValue<double>(); // 2 ShowStaticValue<double>(); // 3 return 0; }
✏️클래스 템플릿과 static멤버변수
-
static 멤버변수 : 선언된 클래스의 객체간 공유가 가능한 변수
- 템플릿 클래스 별로 static 멤버함수를 유지하게 된다
-
예제
#include <iostream> using namespace std; template <typename T> class SimpleStaticMem { private: static T mem; public: void AddMem(int num) { mem+=num; } void ShowMem() { cout<<mem<<endl; } } ; template <typename T> T SimpleStaticMem<T>::mem=0; // static 멤버 초기화 문장 /* template<> long SimpleStaticMem<long>::mem=5; // static 멤버의 초기화에 대해서도 특수화를 진행할 수 있다. */ int main(void) { SimpleStaticMem<int> obj1; SimpleStaticMem<int> obj2; obj1.AddMem(2); obj2.AddMem(3); obj1.ShowMem(); SimpleStaticMem<long> obj3; SimpleStaticMem<long> obj4; obj3.AddMem(100); obj4.ShowMem(); return 0 ; }
✏️template, template<>
-
템플릿 관련 정의에는
template<typename T>
혹은template<>
와 같은 선언을 써서, 컴파일러에게 템플릿을 정의하고 있음을 알린다template
: 템플릿 정의에 T가 등장하는 경우, 이 선언을 통해 T가 의미하는 바를 알려야한다 template<> : 특수화된 템플릿의 경우, T라는 문자가 등장하지 않지만 템플릿의 정의임을 알려야 하기 때문에 이 선언을 명시한다. -
static 멤버의 초기화에 대해서도 특수화를 진행할 수 있다.
template<>
long SimpleStaticMem::mem=5;