C++ 클래스 템플릿(Class Template)

프로그래밍/C++ 2019.10.31 댓글 Plorence

클래스 템플릿을 학습하기 전, 반드시 함수 템플릿을 공부하시는 것을 권합니다.

저번에는 함수 템플릿이었지만, 이번에는 클래스 템플릿입니다.

둘 다 똑같은 템플릿이고 비슷하기도 합니다.

다만 이번에는 함수가 아닌 클래스입니다.

 

클래스 템플릿 정의

template<class Type> //class 대신 typename도 가능
class Array {
       
};

함수 템플릿처럼 클래스 정의 위에 template<class Type>을 써주면 됩니다.

이때 키워드 class는 데이터형을 값으로 받아들이는 어떤 변수의 데이터형 이름의 역할을 합니다 Type은 그 변수의 이름을 나타냅니다.

키워드 class는 템플릿 매개변수의 타입이 반드시 클래스여야 한다는 것은 아닙니다.

단지 실제 데이터형으로 대체되는 포괄 데이터형의 지정자 역할을 한다는 뜻입니다.

(typename 키워드는 최신 컴파일러에서 사용이 가능합니다.)

(확인해보니 C++98에서도 됩니다. 안 되는 컴파일러가 없을 겁니다.)

 

템플릿 멤버 함수

클래스 템플릿과 마찬가지로 멤버 함수를 템플릿으로 만들려면 똑같이 해줘야 합니다.

template<class Type> 
class Array {
public:
       void Show1();
       void Show2() { //인라인 정의
       }
};
template <class Type>
void Array<Type>::Show1() {
}

다만 인라인 정의는 템플릿 제한자와 클래스 제한자를 생략할 수 있습니다.

왜냐하면 클래스 범위 내에서는 Array가 Array<Type>의 약식 표기이기 때문입니다.

그래서 외부에서 정의하는 경우에 다 써주어야 합니다.

 

이렇게 클래스를 만들었으면 템플릿들이 클래스와 멤버 함수 정의가 아니라는 것을 알아야 합니다.

어떤 템플릿의 특별한 현실화를 구체화(instantiation)또는 특수화(specialization)라고 합니다.

그리고 템플릿들은 함수가 아니기 때문에 모든 템플릿 관련 정보를 헤더 파일에 집어넣는 방식이 가장 간단합니다.

(개별적으로 컴파일이 불가능하기 때문.)

 

템플릿 클래스 사용

클래스 템플릿을 설계했다고 템플릿 클래스가 생성되지 않는다는 사실을 알았습니다.

템플릿 클래스를 생성하려면 구체화 요청을 해야 합니다.(모든 데이터형에 대하여 미리 생성할 수 없기 때문에)

템플릿 클래스형의 객체를 선언하되 포괄적인 데이터형 이름을 구체적인 데이터형으로 대체해야 합니다.

Array<int> arr1;
Array<double> arr2;

이 선언을 발견했을 때 컴파일러는 두 개의 서로 다른 클래스 선언과 두 개의 서로 다른 클래스 메서드들의 집합을 생성할 것입니다.

첫 번째 선언은 위에 본 Type을 모두 int로 대체할 것이고 

두 번째 선언은 Type을 모두 double 대체할 것입니다.

template<class Type> //class 대신 typename도 가능
class Array {
private:
       Type arr[1000];
public:
       void Show1() {
       }
       void Show2() {
       }
};
template <class Type>
void Array<Type>::Show1() {
}

이러한 클래스가 있을 때 Array<int>는 Type arr[1000]를 int arr[1000]으로 만들 겁니다.

double도 마찬가지입니다.

이러한 Type과 같은 포괄적인 데이터형 식별자를 데이터형 매개변수(type parameter)라고 하는데 일반 매개변수와 차이점 은값이 아닌 데이터형을 대입합니다.

첫 번째 선언인 Array<int>에서 데이터형 매개변수 Type은 데이터형인 int라는 값을 가집니다.

이때 사용하려는 데이터형을 명시적으로 전달해줘야 한다는 점에서 함수 템플릿과 다릅니다.

(함수 템플릿은 파라미터의 타입에 의해 결정됨.)

 

데이터형이 아닌 매개변수

클래스를 정의할 때 데이터형 매개변수가 아닌 일반적인 매개변수(특정 데이터 타입)도 사용이 가능합니다.

이러한 매개변수를 이터형이 아닌 매개변수(non-type argument) 또는 수식 매개변수(expression argument)라고 합니다.

앞으론 이것을 수식 매개변수라고 하겠습니다.

객체를 선언할 때 데이터형은 물론 배열 길이도 전달한다고 생각해봅시다.

template<class Type,int N> //class 대신 typename도 가능
class Array {
private:
       Type arr[N];
public:
};

이러한 클래스 템플릿이 있다고 가정하고 아래와 같이 선언하면

Array<int,100> arr1;

Type은 int로 대체하고 N은 100으로 대체합니다.

이게 가능한 이유는 클래스 템플릿은 정의를 한 게 아닌 틀만 짜 놓은 형태이기 때문입니다.

클래스 템플릿이 생성될 때 배열 길이도 필수로 전달해야 하기 때문에 문제가 발생하지 않습니다.

 

수식 매개변수의 제약

수식 매개변수는 정수형, 열거형, 참조, 포인터가 수식 매개변수로 될 수 있습니다.

그래서 double du는 안되고 double * du 또는 double & du만 가능합니다.

그리고 수식 매개변수의 값을 변경하거나 주소를 얻을 수 없습니다.

그래서 구체화할 때 수식 매개변수에 사용되는 값은 상수여야 됩니다.

Array<int,n++> a; //불가능
Array<int,&n> b; //불가능

 

수식 매개변수로 배열의 길이를 정할 때

Array<int,100> arr1;
Array<int, 101> arr2;

이 두 선언은 서로 다른 두 개의 클래스를 생성합니다.

반면에 생성자를 통해 배열 길이를 넘겨줬을 경우 클래스 선언을 하나만 합니다.

왜냐하면 생성자를 통해서 넘겨주는 경우 이미 앞에 명시적으로 전달해준 매개변수에 대한 클래스가 생성되어 있기 때문입니다.

그래서 위에 경우는 static멤버를 공유하지 않고, 아래의 경우는 static 멤버를 공유합니다.

(이때 공유한다는 말은 다른 객체에서 값을 변경해도 또 다른 객체에서도 영향이 있다는 말입니다.)

Array<int> arr1 = Array<int>(100); //생성됨
Array<int> arr2 = Array<int>(101); //이미 생성되어서 따로 만들 필요가 없음

쉽게 설명하면 데이터형 매개변수에 의해 시그니처가 결정됩니다.

이 시그니처 동일한 클래스들은 static 변수가 공유됩니다.

위 클래스의 경우 둘 다 시그니처를 int로 볼 수 있습니다.

수식 매개변수로 길이를 정하면 자동 변수들을 관리하는 스택 메모리를 사용하므로 크기가 작은 배열들을 많이 사용하는 경우에 더 빠른 실행 속도를 제공합니다.

하지만 정적이기 때문에 배열의 크기를 임의로 변경이 불가능합니다.

 

여러 개의 데이터형 매개변수 사용

클래스 템플릿에서는 여러 개의 데이터형 매개변수 사용이 가능합니다.

template<class Type,class Type2> //class 대신 typename도 가능
class Array {
private:
       Type arr[1000];
       Type2 arr2[1000];
public:
};

그러면 선언할 때 명시적으로 전달해줘야 하는 데이터형도 데이터형 매개변수에 맞춰서 전달해야 합니다.

 

디폴트 데이터형 매개변수

일반적인 디폴트 매개변수와 비슷한 의미입니다만 디폴트 데이터형 매개변수는 값이 아닌 데이터형을 써야 합니다.

명시적으로 전달해줄 때 데이터형을 생략했을 때 디폴트로 지정한 데이터로 대체합니다.

template<class Type, class Type2 = int> //class 대신 typename도 가능
class Array {
private:
       Type arr[1000];
       Type2 arr2[1000];
public:
};
int main(void) {
       Array<int, int> arr1; //Type = int,Type2 = int
       Array<int> arr2; //Type = int,Type2 = int
};

데이터형 매개변수가 아닌 수식 매개변수도 디폴트 매개변수가 가능합니다.

함수 템플릿 매개변수에는 디폴트 값을 제공할 수 없습니다.

그러나 클래스 템플릿과 함수 템플릿 두 경우 모두, 데이터형이 아닌 매개변수에 대해서는 디폴트 값을 제공할 수 있습니다.

 

댓글