전 챕터에서는 Point클래스를 템플릿화를 하였는데 템플릿을 기반으로 Point(int) << (부등호 기호가 안먹어서 이렇게 표현) 저러한 형식의 템플릿 클래스의 객체를 저장하는 방법에 대해서 알아본다.
PointTemplate.h
#ifndef __POINT_TEMPLATE_H__
#define __POINT_TEMPLATE_H__
template <typename T>
class Point
{
private:
T xpos, ypos;
public:
Point(T x = 0, T y = 0);
void ShowPosition() const;
};
template <typename T>
Point<T>::Point(T x, T y) : xpos(x), ypos(y)
{
}
template <typename T>
void Point<T>::ShowPosition() const
{
cout << '[' << xpos << ", " << ypos << ']' << endl;
}
#endif
ArrayTemplate.h
#ifndef __ARRAY_TEMPLATE_H_
#define __ARRAY_TEMPLATE_H_
#include <iostream>
#include <cstdlib>
using namespace std;
template <typename T>
class BoundCheckArray
{
private:
T* arr;
int arrlen;
BoundCheckArray(const BoundCheckArray& arr) { }
BoundCheckArray& operator=(const BoundCheckArray& arr) { }
public:
BoundCheckArray(int len);
T& operator[] (int idx);
T operator[] (int idx) const;
int GetArrLen() const;
~BoundCheckArray();
};
template <typename T>
BoundCheckArray<T>::BoundCheckArray(int len) : arrlen(len)
{
arr = new T[len];
}
template <typename T>
T& BoundCheckArray<T>::operator[] (int idx)
{
if (idx < 0 || idx >= arrlen)
{
cout << "Array index out of bound exception" << endl;
exit(1);
}
return arr[idx];
}
template <typename T>
T BoundCheckArray<T>::operator[](int idx) const
{
if (idx < 0 || idx >= arrlen)
{
cout << "Array index out of bound exception" << endl;
exit(1);
}
return arr[idx];
}
template <typename T>
int BoundCheckArray<T>::GetArrLen() const
{
return arrlen;
}
template <typename T>
BoundCheckArray<T>::~BoundCheckArray()
{
delete[] arr;
}
#endif
TBoundArrayMain.cpp
#include <iostream>
#include "PArrayTemplate.h"
#include "PointTemplate.h"
using namespace std;
int main(void)
{
BoundCheckArray<Point<int>> oarr1(3);
oarr1[0] = Point<int>(3, 4);
oarr1[1] = Point<int>(5, 6);
oarr1[2] = Point<int>(7, 8);
for (int i = 0; i < oarr1.GetArrLen(); i++)
oarr1[i].ShowPosition();
BoundCheckArray<Point<double>> oarr2(3);
oarr2[0] = Point<double>(3.14, 4.31);
oarr2[1] = Point<double>(5.09, 6.07);
oarr2[2] = Point<double>(7.82, 8.54);
for (int i = 0; i < oarr1.GetArrLen(); i++)
oarr2[i].ShowPosition();
typedef Point<int>* POINT_PTR;
BoundCheckArray<POINT_PTR> oparr(3);
oparr[0] = new Point<int>(11, 12);
oparr[1] = new Point<int>(13, 14);
oparr[2] = new Point<int>(15, 16);
for (int i = 0; i < oarr1.GetArrLen(); i++)
oparr[i]->ShowPosition();
delete oparr[0]; delete oparr[1]; delete oparr[2];
return 0;
}
앞에서 배운것과 같이 템플릿 클래스 또한 C++의 일반적인 문법을 통해 위의 예시와 같이 확장이 가능하다.
클래스 템플릿을 특수화 하는 이유는 특정 자료형을 기반으로 생성된 객체에 대해, 구분이 되는 다른 행동양식을 적용하기 위해서!
ClassTemplateSpecialization.cpp
#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:
SimipleDataWrapper(T data)
{
mdata = data;
}
void ShowDataInfo(void)
{
cout << "Data: " << mdata << endl;
}
};
template <>
class SimpleDataWrapper<char*>
{
private:
char* mdata;
public:
SimpleDataWrapper(const 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();
SimpleDataWrapper<char*> swrap("Class Template Specialization");
swrap.ShowDataInfo();
SimpleDataWrapper<Point<int>> poswrap(3, 7);
poswrap.ShowDataInfo();
return 0;
}
위의 예시에서는 char*형과 Point(int)형에 대해서 특수화 하는 방법을 보여준다. 특수화를 따로 정의해주지 않은것은 SimpleDataWrapper 기본 템플릿이 적용되어 만들어진다. 위 예제는 실행을 해보았을 때 컴파일러마다 에러를 발생하는 경우도 있고 그렇지 않은 경우도 있다.
클래스 템플릿은 부분특수화 또한 가능한데 간단한 예시는 다음과 같다.
template <typename T1, typenmae T2>
class Mysimple{...}
template <>
class Mysimple<char, int> {....}
template <>
class Mysimple<T1, int> {....}
class Mysimple(T1, int)에서 보는 바와 같이 T2하나에 대해서만 부분적으로 특수화가 가능하다.
NonTypeTemplateParam.cpp
#include <iostream>
using namespace std;
template <typename T, int len>
class SimpleArray
{
private:
T arr[len];
public:
T& operator[](int idx) { return arr[idx]; }
SimpleArray<T, len>& operator=(const SimpleArray<T, len>& ref)
{
for (int i = 0; i < len; i++)
arr[i] = ref.arr[i];
return *this;
}
};
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;
}
배열의 길이를 템플릿 클래스로 전달한 경우 길이에 따라 서로 다른형이 생성이 되어 길이가 다른 두 배열간 객체간 대입은 허용되지 않는다. 하지만 생성자로 길이를 전달하였을 경우 길이가 같은 배열에 대해서만 대입을 허용하기 위해 추가적인 코드가 필요하다.
템플릿 매개변수는 함수와 마찬가지로 매개변수의 디폴트 값 지정이 가능하다.
TemplateParamDefaultValue.cpp
#include <iostream>
using namespace std;
template <typename T= int, int len = 7>
class SimpleArray
{
private:
T arr[len];
public:
T& operator[] (int idx) { return arr[idx]; }
SimpleArray<T, len>& operator=(const SimpleArray<T, len>& ref)
{
for (int i = 0; i < len; i++)
arr[i] = ref.arr[i];
return *this;
}
};
int main(void)
{
SimpleArray<> arr;
for (int i = 0; i < 7; i++)
arr[i] = i + 1;
for (int i = 0; i < 7; i++)
cout << arr[i] << " ";
cout << endl;
return 0;
}
템플릿 매개변수에 디폴트 값이 지정되어도 객체를 생성할때 <>기호는 꼭 넣어주어야 한다.
static 지역변수는 템플릿 함수 별로 각각 존재하게 된다.
예를들어 ShowStaticValue라는 함수에 static T num = 0이라는 변수가 존재 시, (int), (double), (long)으로 자료형을 템플릿에 전달하였을 시 각각 static변수가 존재함.
static 멤버변수는 변수가 선언된 클래스의 객체간 공유가 가능한 변수이다.
ClassTemplateStaticMem.cpp
#include <iostream>
using namespace std;
template <typename T>
class SimpleStaticMem
{
private:
static T mem;
public:
void AddMem(T num) { mem += num; }
void ShowMem() { cout << mem << endl; }
};
template <typename T>
T SimpleStaticMem<T>::mem = 0; // 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;
}
객체간 공유가 가능하므로 출력결과는 각각 5와 100이 나오게 된다.
template <class T>
T SimpleStaticMem<T>::mem = 0;
template <>
long SimpleStaticMem<long>::mem = 5;
클래스 템플릿 정의의 일부인 초기화문을 대상으로도 특수화가 가능하다.