[C++] Chapter 14 - 템플릿(Template) 2

Lee Jeong Min·2021년 1월 23일
0

Cpp

목록 보기
14/16
post-thumbnail

Chapter 13에서 공부한 내용의 확장

Point 클래스 테믈릿과 배열 클래스 템플릿

전 챕터에서는 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++의 일반적인 문법을 통해 위의 예시와 같이 확장이 가능하다.


14-2 클래스 템플릿의 특수화(Class Template Specialization)

클래스 템플릿 특수화

클래스 템플릿을 특수화 하는 이유는 특정 자료형을 기반으로 생성된 객체에 대해, 구분이 되는 다른 행동양식을 적용하기 위해서!

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하나에 대해서만 부분적으로 특수화가 가능하다.


14-3 템플릿 인자

템플릿 매개변수에 변수의 선언이 올 수 있다.

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;
}

템플릿 매개변수에 디폴트 값이 지정되어도 객체를 생성할때 <>기호는 꼭 넣어주어야 한다.


14-4 템플릿과 static

함수 템플릿과 static 지역변수

static 지역변수는 템플릿 함수 별로 각각 존재하게 된다.
예를들어 ShowStaticValue라는 함수에 static T num = 0이라는 변수가 존재 시, (int), (double), (long)으로 자료형을 템플릿에 전달하였을 시 각각 static변수가 존재함.

클래스 템플릿과 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이 나오게 된다.

템플릿 static 멤버변수 초기화의 특수화

template <class T>
T SimpleStaticMem<T>::mem = 0;

template <>
long SimpleStaticMem<long>::mem = 5;

클래스 템플릿 정의의 일부인 초기화문을 대상으로도 특수화가 가능하다.

profile
It is possible for ordinary people to choose to be extraordinary.

0개의 댓글