템플릿은 보관할 자료형을 인수로 하여 컨테이너 클래스나 함수를 생성할 수 있게 함으로써 개별적 자료형 각각에 대해 프로그램을 중복적으로 작성하는 노력을 줄일 수 있게 한다.
컨테이너 클래스란 다른 객체를 저장하는 클래스로서, 예를 들어 스택, 큐, 배열, 리스트 등을 들 수 있다.
그러나 동일한 유형의 컨테이너일지라도 저장하고자 하는 객체의 유형이 다르면 이에 맞게 새로운 클래스를 선언해야 한다. 이러한 코드 중복을 방지할 수 있는 방법을 템플릿이라 한다.
템플릿은 클래스를 선언할 때 객체의 자료형을 고려하지 않고, 객체의 자료형을 인수로 처리한다. 컨테이너 클래스를 선언할 때 특정 자료형이 아닌 일반적인 자료형을 대상으로 하는 템플릿을 작성한다. 특정 자료형을 위한 컨테이너 객체가 필요할 때 그 자료형을 템플릿의 매개변수로 전달하여 그 자료형에 해당되는 클래스가 자동적으로 선언되게 한다.
자바를 사용해봤다면 제네릭과 비슷하다. 동적으로 타입을 정해주어 해당 타입에 맞는 클래스가 되는 것과 같다.
클래스 템플릿은 다음과 같은 형식으로 선언한다.
template <templateParameters> class ClassTemplateName{...};
스택 템플릿을 예제로 생성한다면 다음과 같다.
#ifndef STACK_TEMPLATE_H_INCLUDED
#define STACK_TEMPLATE_H_INCLUDED
#include <iostream>
using namespace std;
template <typename T>
class Stack {
T* buf;
int top;
int size;
public:
Stack(int s){}
virtual ~Stack(){}
bool full()const {}
bool empty()const {}
void push(const T& a){}
void push(T&& A){}
T&& pop(){}
};
template <typename T> Stack<T>::Stack(int s) :size(s), top(s)
{
buf - new T[s];
}
template <typename T> Stack<T> :: ~Stack() {
delete[] buf;
}
template <typename T> bool Stack<T> ::full() const {
return !top;
}
template <typename T> bool Stack<T> ::empty() const {
return top == size;
}
template <typename T> void Stack<T> ::push(const T& a) {
buf[--top] = a;
}
template <typename T> void Stack<T> ::push(T&& a)
{
buf[--top] = move(a);
}
template <typename T> T&& Stack<T> ::pop() {
return move(buf[top++]);
}
#endif // !STACK_TEMPLATE_H_INCLUDED
해당 소스코드는 typename T라는 하나의 템플릿 매개변수를 사용하는 클래스 템플릿 Stack을 선언한 것이며
클래스 템플릿에 속한 멤버함수를 클래스 템플릿 선언 외부에서 하는 형식이다.
T* buf는 매개변수에 저장된 T라는 이름으로 임의의 자료형의 포인터를 선언한 것이다.
push는 l-value가 인수로 전달되어있을 때 복사를 하도록 했으며, r-value값이 인수로 전달되었을 때는 임시 객체의 내용을 이동하도록 했다.
* 보통 클래스를 선언할 때에는 클래스 선언문은 헤더 파일에 작성하고 정의는. cpp파일에 작성했지만 클래스 템플릿의 경우는 다르다. 선언문 외부의 멤버 함수들도 헤더 파일에 작성한다. 클래스 템플릿은 클래스가 아니라 클래스를 만드는 템플릿이며, 멤버함수의 선언도 직접 함수를 정의하지 않고 함수에 대한 템플릿을 선언만 하는 것이기 때문이다.
클래스 템플릿에 대한 객체를 정의할 때에는 실제 클래스 템플릿이 취급할 변수형 또는 클래스의 이름을 인수로 전달하여 정의한다.
ClassTemplateName<ClassName> objName(constrArgs);
ClassTemplateName : 템플릿 매개변수에 전달할 클래스 또는 자료형 이름
objName : 정의할 객체의 이름
constrArgs : 생성자에 전달할 인수
해당 규칙을 보고 char형 stack과 int형 stack객체를 생성하는 것은 다음과 같다.
int main()
{
Stack<char> sc(100);
sc.push('a');
......
STack<int> si(50);
si.push(5);
return 0;
}
함수 템플릿
클래스 템플릿을 사용한다는 것은 멤버함수 템플릿을 사용한다는 것을 암시적으로 포함하고 있다. 그러나 클래스의 멤버 함수가 아닌 전역 함수에 대한 템플릿도 만들 수 있으며, 이것을 함수 템플릿이라고 한다.
만약 함수 템플릿의 이름이 같은 함수가 존재하는 걸 위해 다중 정의를 할 수 있다. 이름이 같은 함수가 여러 개 있을 때에는 함수 이름과 인수들의 데이터 형이 정확하게 일치하는 함수가 먼저 선택되어 호출되고, 그러한 함수가 없을 때에는 호출한 함수와 정확하게 일치시키는 변환이 가능한 함수 템플릿이 호출된다.
댓글