공부 중 메모/C++ STL(Book : 뇌를 자극하는 STL)

2_템플릿

라이피 (Lypi) 2018. 7. 27. 02:01
반응형
#include <iostream>
using std::cout;
using std::endl;
using std::cin;

#include <string>
using std::string;

//first, second, third, forth, fifth_basic, fifth_default, sixth, seventh, eighth, nineth, problem_1, problem_2, problem_3
#define problem_3

//함수 템플릿 : 타입이 다른 함수를 만들어내는 틀
//클래스 템플릿 : 타입이 다른 클래스를 만들어내는 틀

//기본적인 자료형에 대한 함수는 함수 오버로딩으로 대응할 수 있지만 사용자 정의 자료형에 대해서는 대응할 수 없다.
//이때 함수 템플릿을 사용하면 사용자 정의 자료형에도 대응하는 함수를 만들어낼 수 있다.

//함수 템플릿은 실제로 사용될 때의 인자값에 따라 실제 함수를 만들어낸다.
//이렇게 만들어진 실제 함수를 템플릿의 인스턴스라고 한다.



//template <typename T>와 template<class T>는 같은 의미를 갖는다.
#ifdef first
template <typename T>
void print(T a, T b)
{
	cout << a << ", " << b << endl;
}

//반환형에도 사용할 수 있다.
template <typename A, class B>
A print_a(A a, B b)
{
	cout << b << endl;
	return a;
}

int main()
{
	print(1, 2);
	print(0.12, 3.45);
	print("abc", "def");

	//템플릿 인스턴스의 타입을 명시한 경우
	print<int>('a', 'b');

	//서로 다른 타입을 매개변수로 받는 템플릿의 인스턴스
	cout << print_a(2, "b") << endl;
}
#endif


//템플릿의 타입으로 포인터나 레퍼런스가 넘어가진 않는다.
#ifdef second

//이 템플릿 함수에는 복사 생성자와 대입 연산자를 지원하는 모든 타입이 올 수 있다.
template <typename T>
void Swap(T &a, T &b)
{
	T temp = a;
	a = b;
	b = temp;
}

int main()
{
	int n1 = 10, n2 = 20;
	double d1 = 0.1, d2 = 0.2;

	cout << n1 << ", " << n2 << endl;
	Swap(n1, n2);
	cout << n1 << ", " << n2 << endl;

	cout << d1 << ", " << d2 << endl;
	Swap(d1, d2);
	cout << d1 << ", " << d2 << endl;
}

#endif


//템플릿의 매개변수로는 타입이름뿐 아니라 기본 자료형도 가능하다.
//이때는 템플릿 매개변수의 인자를 명시적으로 전달해줘야한다. 
#ifdef third

template <typename T, int size>
void printArr(T* arr)
{
	for (int i = 0; i < size; i++) {
		cout << "[" << i << "] : " << arr[i] << endl;
	}
	cout << endl;
}

int main()
{
	int arr1[5] = { 1,2,3,4,5 };
	printArr<int, 5>(arr1);

	double arr2[3] = { 0.1, 0.2, 0.3 };
	printArr<double, 3>(arr2);
}

#endif


//함수 템플릿 특수화
#ifdef forth

class point
{
	int x;
	int y;

public:
	explicit point(int _x = 0, int _y = 0) : x(_x), y(_y) {}
	void print() const 
	{ 
		printf("(%d,%d)", x, y);
	}
};

//일반화 함수 템플릿
template <typename T>
void print(T a)
{
	cout << a << endl;
}


//특수화 함수 템플릿
//template <point>
//void print(point a) 
//가 더 명시적인 코드이다.

template <>
void print(point a)
{
	cout << "print 특수화 버전: ";
	a.print();
}

int main()
{
	int n = 10;
	double d = 2.5;
	point pt(2, 3);

	print(n);   // print<int>(n)    일반화 버전
	print(d);   // print<double>(d) 일반화 버전
	print(pt);  // print<point>(pt) 특수화 버전

	//하지만 함수 템플릿은 특수화가 아닌 오버로딩이 더 우선시된다.
	//즉 오버로딩으로 처리할 수 있다.
}
#endif


//클래스 템플릿
#ifdef fifth_basic

template <typename T>
class Array
{
	T*  buf;
	int size;      // 원소의 개수
	int capacity;  // 저장 가능한 메모리 크기

public:
	explicit Array(int cap = 100) 
		: buf(0), size(0), capacity(cap) 	
	    { buf = new T[capacity]; }
	virtual  ~Array()
	    { delete[] buf; }

	void   insert(T data)                  { buf[size++] = data; }
	T      operator[] (int idx) const      { return buf[idx]; }
	int    GetSize() const                 { return size; }
};

int main()
{
	Array<int> iarr;
	iarr.insert(1);
	iarr.insert(2);
	iarr.insert(3);

	for (int i = 0; i < iarr.GetSize(); i++) {
		cout << iarr[i] << " ";
	}
	cout << endl;

	Array<double> darr;
	darr.insert(0.1);
	darr.insert(0.2);
	darr.insert(0.3);

	for (int i = 0; i < darr.GetSize(); i++) {
		cout << darr[i] << " ";
	}
	cout << endl;

	Array<string> sarr;
	sarr.insert("ABC");
	sarr.insert("DEF");
	sarr.insert("GHI");

	for (int i = 0; i < sarr.GetSize(); i++) {
		cout << sarr[i] << " ";
	}
	cout << endl;
}
#endif


//디폴트 매개변수 값을 갖는 클래스 템플릿
#ifdef fifth_default

template<typename T = int, int capT = 100>
class Array
{
	T*   buf;
	int  size;
	int  capacity;

public:
	explicit Array(int cap = capT)
		: buf(0), size(0), capacity(cap)
	{
		buf = new T[capacity];
	}
	virtual  ~Array()
	{
		delete[] buf;
	}

	void   insert(T data) { buf[size++] = data; }
	T      operator[] (int idx) const { return buf[idx]; }
	int    GetSize() const { return size; }
};

int main()
{
	Array<> iarr; //디폴트 매개변수 값 사용.
	iarr.insert(1);
	iarr.insert(2);
	iarr.insert(3);

	for (int i = 0; i < iarr.GetSize(); i++) {
		cout << iarr[i] << " ";
	}
	cout << endl;

	Array<double> darr; //디폴트 매개변수 값 사용.
	darr.insert(0.1);
	darr.insert(0.2);
	darr.insert(0.3);

	for (int i = 0; i < darr.GetSize(); i++) {
		cout << darr[i] << " ";
	}
	cout << endl;

	Array<string, 10> sarr; //디폴트 매개변수 값 사용.
	sarr.insert("a");
	sarr.insert("b");
	sarr.insert("c");

	for (int i = 0; i < sarr.GetSize(); i++) {
		cout << sarr[i] << " ";
	}
	cout << endl;
}
#endif


//클래스 템플릿 특수화
#ifdef sixth
template <typename T>
class ObjectInfo
{
	T data;

public:
	ObjectInfo(const T& d)
	{
		data = d;
	}

	void print()
	{
		cout << "타입 : " << typeid(data).name() << endl;
		cout << "크기 : " << sizeof(data) << endl;
		cout << " 값  : " << data << endl;
		cout << endl;
	}
};

template <>
class ObjectInfo<string>
{
	string data;

public:
	ObjectInfo(const string& d)
	{
		data = d;
	}

	void print()
	{
		cout << "타입 : " << "string" << endl;
		cout << "크기 : " << data.size() << endl;
		cout << " 값  : " << data << endl;
		cout << endl;
	}
};

int main()
{
	ObjectInfo<int> d1(10);
	d1.print();

	ObjectInfo<double> d2(5.5);
	d2.print();

	ObjectInfo<string> d3("h");
	d3.print();
}

#endif

//stl을 위한 템플릿 예제
//매개변수로 전달된 함수 포인터의 타입을 컴파일러가 유추할 수 없으므로
//함수 템플릿으로 만들어진 함수의 타입은 명시적으로 지정해줘야 한다.
#ifdef seventh
//begin은 배열의 시작 주소, end는 배열의 끝 주소, pf는 클라이언트 함수 포인터를 의미.
template <typename IterT, typename Func>
void For_each(IterT begin, IterT end, Func pf)
{
	while (begin != end) {
		pf(*begin++);
	}
}

//함수 객체로 변경
template <typename T>
struct print
{
	string sep; //출력 구분자 정보
public:
	explicit print(const string& s = " ") : sep(s) { }
	void operator() (T data) const
	{
		cout << data << sep;
	}
};

int main()
{
	int iarr[5] = { 1,2,3,4,5 };
	For_each(iarr, iarr + 5, print<int>()); //정수 출력
	cout << endl;

	string sarr[3] = { "a","b","c" };
	For_each(sarr, sarr + 3, print<string>()); //정수 출력
	cout << endl;
}
#endif

//반환 타입과 매개변수 타입을 클라이언트가 결정할 수 있는 함수 객체
#ifdef eighth
template <typename RetType, typename ArgType>
class Functor
{
public:
	RetType operator() (ArgType data)
	{
		cout << data << endl;
		return RetType();
	}
};

int main()
{
	Functor<void, int> functor_a;
	functor_a(10);

	Functor<bool, string> functor_b;
	functor_b("Hello");
}

#endif

//stl의 Pair 클래스 템플릿 구현
#ifdef nineth
using std::pair;

template <typename T1, typename T2>
struct Pair
{
	T1 first;
	T2 second;
	Pair(const T1& ft, const T2& sd) : first(ft), second(sd) {};
};

int main()
{

	//직접 구현한 Pair 클래스 템플릿
	Pair<int, int> p1(10, 20);
	cout << p1.first << ", " << p1.second << endl;
	cout << endl;

	Pair<int, string> p2(1, "one");
	cout << p2.first << ", " << p2.second << endl;
	cout << endl;

	//STL의 pair
	pair<int, int> p3(10, 20); 
	cout << p3.first << ", " << p3.second << endl;
	cout << endl;

	pair<int, string> p4(1, "one");
	cout << p4.first << ", " << p4.second << endl;
}
#endif

//문제 풀이_1 (함수 템플릿 작성하기)
#ifdef problem_1
//배열의 원소를 복사하는 함수 템플릿 copy()를 작성하세요.

template<typename T>
void copy(T* dst, T* src, int size )
{
	for (int i = 0; i < size; i++) {
		dst[i] = src[i];
	}
};

int main()
{
	int arr1[5] = { 1,2,3,4,5 };
	int arr2[5] = { 0, };

	copy(arr2, arr1, 5);

	cout << arr2[3] << endl;
}

#endif

//문제 풀이_2 (스택 클래스 구현하기)
#ifdef problem_2

template <typename T>
class Stack
{
	T arr[100];
	int door;

public:
	Stack()
	{
		door = -1;
	}

	void Push(T data)
	{
		arr[++door] = data;
	}

	T Pop()
	{
		return arr[door--];
	}

	bool Empty()
	{
		if (door == -1) {
			return true;
		}
		else {
			return false;
		}
	}
};

int main()
{
	Stack<int> st;

	st.Push(10);
	st.Push(20);
	st.Push(30);

	if (!st.Empty()) { cout << st.Pop() << endl; }
	if (!st.Empty()) { cout << st.Pop() << endl; }
	if (!st.Empty()) { cout << st.Pop() << endl; }
}
#endif

//문제 풀이_3 (큐 클래스 구현하기)
#ifdef problem_3

template <typename T>
class Queue
{
	T arr[100];
	int in;
	int out;

public:
	Queue()
	{
		in = -1;
		out = -1;
	}

	void Push(T data)
	{
		arr[++in] = data;
	}

	T Pop()
	{
		return arr[++out];
	}

	bool Empty()
	{
		if (in == out) {
			return true;
		}
		else {
			return false;
		}
	}
};

int main()
{
	Queue<int> q;

	q.Push(10);
	q.Push(20);
	q.Push(30);

	if (!q.Empty()) { cout << q.Pop() << endl; }
	if (!q.Empty()) { cout << q.Pop() << endl; }
	if (!q.Empty()) { cout << q.Pop() << endl; }
}
#endif


반응형