Recommanded Free YOUTUBE Lecture: <% selectedImage[1] %>

Contents

C++ 템플릿

많은 C++ 프로그램들이 스택(:12), 큐(:12), 리스트(:12)와 같은 일반적인 자료구조를 사용한다. 예컨데, 메시지를 큐로 관리하는 프로그램이 있다고 가정해보자. 이 메시지 큐는 메시지를 원소로 사용하는 다른 프로그램에서도 활용할 수 있을 것이다. 만약 클래스로 만들었다면, 자신의 상황에 맞게 Copy, find, replace 등의 메서드를 구현해서 사용할 수 있을 것이다. 이는 객체지향(:12) 프로그래밍의 재 사용성을 훌륭하게 지원하는 것처럼 보인다.

그러나 자료형이 다를 경우 재 사용성은 물건너 간다. 클래스는 동일한 자료형에 대해서는 훌륭한 재 사용성을 보여주지만, 자료형이 다를 경우에는 그렇지 않다. 만약 클래스를 자료구조로 받는 큐가 필요하다면, 해당 자료구조에 맞게 (많은 부분을)새로 구현해야 할 것이다. 클래스 명이 달라질 수도 있을 것이다. 물론 상속과 다형성 같은 개체지향적 요소 혹은 패턴(:12)을 이용할 수 있지만 제너릭하지 못하다.

템플릿(Template)은 제너릭프로그래밍을 가능하도록 해주는 유용한 도구다. C++의 탬플릿은 자료형이 다를 지라도 이를 최소한의 노력으로 재사용할 수 있도록 도와준다.

클래스 탬플릿

클래스 탬플릿 만들기

클래스 탬플릿을 구현하는 방법은 다음과 같다. 클래스에 template 키워드만 더 해주면 된다.
template <class T>
class Stack
{
private:
	int size;
	int top;
	T* stackPtr;	
public:
	Stack(int = 10);
	~Stack() {delete[] stackPtr;}
	int push_back(const T&);
	int pop(T&);
	int isFull(){ return top == size -1;};
	int end(){ return top == -1;}
};
T는 자료형을 의미하는데, 어떤 형태라도 사용할 수 있음을 의미한다. 만약 개발자가 int 자료형을 사용한다면 T는 int로 치환될 것이고 이 스택은 int형 자료를 위한 Stack로 사용할 수 있다게된다. 이게 가능한 이유는 컴파일 시간에 자료형을 판단해서 코드를 만들기 때문이다.

클래스 탬플릿 사용

먼저 클래스 탬플릿을 만든다.
// mytemp.h
template <class T>
class a
{
public:
	a(){}
	int push(const T&)
	{
		// ...
	}
	~a(){}
}

이제 사용하면 된다.
#include "mytemp.h"

int main()
{
	a<int> inta;
	a<string> stringa;
}

설정 클래스

설정 값을 읽어오는 클래스를 만든다고 가정해보자. 설정을 가져오는 API는 이미 만들어져 있으며, 이를 호출해서 사용한다고 가정해 보자. 이경우 설정값은 대부분 구조체를 void *로 형변환해서 사용할 거다.

이러한 경우 다음과 같이 클래스를 만들고 이를 상속해서 사용한다면, 범용적으로 사용가능한 설정 클래스를 개발할 수 있을 것이다.
template <class T> class ConfigBase
{
private:
	vector<T *> cfgVector;
public:
	ConfigBase() {}
	~ConfigBase()
	{
		cfgVector.clear();
	}

	// 여기에서 설정 API를 호출하고 그 값은 cfgVector에 넣는다.
	// 설정을 읽어오는 외부 함수는 configread 라고 하겠다. 
	// 설정을 저장하기 위한메모리를 할당하기 때문에
	// 설정값을 사용하지 않을 거라면 configfree 함수를 호출 메모리를 해제해야 한다. 
	int ReadCfg(const char *cfgname)
	{
		T *a;
		while( (a = (T *)configread()) != NULL)
		{
			cfgVector.push_back(a);
		}
	}
	int CfgSize()
	{
		return cfgVector.size();
	}
	vector<T> *Fetch()
	{
		return &cfgData;
	}

	void CloseFetch()
	{
		configfree();
	}
	virtual int LoadCfg(const char *cfgname) = 0;
};

이제 다음과 같이 ConfigBase를 상속받아서 사용하면 된다. 자료형에 상관없이 설정을 읽을 수 있다.
class appCfg : ConfigBase<struct clientInfo>
{
private:
	vector<clientInfo> *cfgList;
public:
	int LoadCfg(const char *cfgname)
	{
		cfgList = Fetch();
		// 이런 저런 작업 후
		CloseFetch();
	}
};

클래스 탬플릿 Specialization

템플릿 클래스도 오버라이딩할 수 있다.

#include <iostream>

using namespace std ;

template <class T>
class stream
{
	public:
		void f() { cout << "stream<T>::f()"<< endl ;}
} ;

template <>
class stream<char>
{
	public:
		void f() { cout << "stream<char>::f()"<< endl ;}
} ;

int main()
{
	stream<int> si ;
	stream<char> sc ;

	si.f() ;
	sc.f() ;
	
	return 0 ;
}
위 예는 stream<char>일때 원래 정의된 f() 함수대신 새로 함수를 오버라이딩해서 사용하는 법을 보여준다. 특정 자료형에 대해서 다른 행동을 정의할 때 사용할 수 있다.

템플릿 클래스 Partial Specialization

템플릿 클래스의 typename을 특정해서, 여러 특화된 템플릿을 동시에 운용할 수도 있다.
include <iostream>

using namespace std;

// Base 템플릿 클래스
template<typename T1, typename T2>
class X
{
public:
    X(){ cout << "Base Template" << endl; }
} ;

//partial specialization
template<typename T1>
class X<T1, int>
{
public:
    X() { cout << "T1, int Template" << endl; }
} ; //C2989 here

int main()
{
    X<char, char> xcc ;
    X<char, int> xii ;

    return 0 ;
}
class X<T1, int>에서 int 타입을 특정해서, 템플릿을 운용하고 있다. 음 템플릿 오버로딩이라고 봐도 될 것 같은데. 참고로 컴파일러에 따라서 partial 템플릿을 지원하지 않는 경우도 있을 수 있다. 예를 들어 Visual C++ 5.0은 지원하지 않는다고 한다. 요즘에야 8.0이상을 사용하고 있으니 문제될 것 같진 않지만 확인은 필요할 것 같다.

템플릿 함수

템플릿을 이용하면, 컴파일 시간에 데이터 형을 정해주므로 범용 함수를 만들 수 있다.
#include <iostream>
#include <stdio.h>
#include <string.h>

using namespace std;

template <class T>
T Sum(T a, T b)
{
    return a + b;
}

template <>
char *Sum(char *a, char * b)
{
    return strcat(a, b);
}

int main()
{
    char a[80]="hello";
    char *b = " joinc";
    cout << Sum(1, 5) << endl;
    cout << Sum(8.1, 5.5) << endl;
    cout << Sum(8.1, 5.5) << endl;
    cout << Sum(a, b) << endl;
}
템플릿을 이용해서 간단하게 범용 덧셈 프로그램을 만들었다. 단 char 문자열 데이터의 경우 덧셈 연산이 불가능하기 때문에, char * 데이터를 처리하기 위한 템플릿 함수를 별도로 정의 했다. 다음은 프로그램 실행 결과다.
6
13.6
13.6
hello joinc

관련글