1. 템플릿
Standard Template Library라는 이름에서 알 수 있듯이 STL은 C++의 템플릿이라는 기능을 이용하여 제네릭 프로그래밍을 실현하고 있다. STL의 모든것은 템플릿으로 작성되어 있기 때문에 STL을 이해하려면 템플릿에 대해 어느정도 이해가 필요하다.템플릿에 대해잘 알고 있는 사람은 이번장을 건너뛰어도 관계가 없심.
템플릿이란?
간단하게 말해서 템플릿은 여러 타입에 대해 동작하는 일반 클래스와 함수를 만들 수 있게 해주는 유용한 도구인거심. 많은 책과 문서들이 탬플릿을 설명하는데 max함수의 예를들어 설명하고 있고 이게 상당히 적절함으로 우리도 max함수를 살펴보면서 이야기를 진행하도록 하겠삼
- int max(int a, int b)
- {
- if(a > b)
- return a;
- return b;
- if(a > b)
- }
함수 템플릿
어려운 방법
여러 타입의 변수에대해 동작하는 max함수를 만드는데는 3가지 방법이 있는데, 하나는 define 매크로를 이용하는 것임다. 가장 어려운 방법이고 가장 인기없는 방법이삼.
- #include <iostream>
- #define define_max(type) type max(type a, type b) { \
- if(a > b) \
- return a; \
- return b; \
- if(a > b) \
- }
- define_max(int);
- define_max(double);
- define_max(char);
- int main(void)
- {
- using namespace std;
- double f = max(3.5, 8.7);
- int i = max(100, 800);
- char ch = max('A', 'Q');
- .....어쩌구 저쩌구....
- return 0;
- }
위의 소스코드는 잘 동작하지만 define 매크로를 이용하는 방법은 큰 함수에서는 문제가 생긴다. 디버깅이 어렵도 매크로를 작성하기도 어렵다. 그래서 보통은 max함수를 오버로딩하는 방법을 선택한다.
- int o_max(int a, int b)
- {
- if(a > b)
- return a;
- return b;
- if(a > b)
- }
- double o_max(double a, double b)
- {
- if(a > b)
- return a;
- return b;
- if(a > b)
- }
- ...어쩌구 저쩌구...
이 방법도 작 동작한다. 위 두가지 방법의 문제는 새로운 타입의 max함수를 사용할 때 마다 매크로를 호출하거나 새로운 함수를 만들어야 한다는 것이다. 특히 오버로딩을 하는 사용하면 필요한 모든 경우에 대해 일일히 함수를 작성해야 한다. 참으로 화나는 일이다. -_-;;
편한 방법
템플릿을 사용하면 위에서 언급된 문제점들을 피할 수 있고 무엇보다 편함. 좋은게 좋은거임 >.<
- #include <iostream>
- template<typename kind>
- kind t_max(kind a, kind b)
- {
- if(a > b)
- return a;
- return b;
- if(a > b)
- }
- int main(void)
- {
- using namespace std;
- int a = t_max(1, 8);
- char ch = t_max('A', 'Q');
- double d = t_max(1.5, 8.9);
- ...어쩌구 저쩌구...
- return 0;
- }
이제 문제는 해결 됐다. 필요한 버젼의 max함수가 알아서 생성되니 우리는 사용만 하면 되는거심. 우리가 max함수에 사용하는 타입이 operator>를 지원하기만 하면 각종 버젼의 max함수를 만들지 않아도 되니 좋지 아니할수가 없심다.
그런데 여기서도 한가지 문제가 생겼으니 문자열에 관한 max함수 인거심. char* 타입은 단순히 > 연산을 사용해서 크기를 비교 할 수 없으니 우리의 템플릿 max함수가 제대로 동작할 수 없심. 이때 사용하는것이 바로 전문화란 것으로 특정한 버젼의 max함수를 명시적으로 선언하는 것이 되겠심다.
- char *t_max(char *a, char *b)
- {
- if(strcmp(a, b) < 0)
- return a;
- return b;
- if(strcmp(a, b) < 0)
- }
이제 max함수는 char* 타입에 대해서도 정확히 동작하게 되었습니다.
클레스 템플릿
템플릿 함수를 만드는 것 처럼 클래스에도 템플릿을 적용할 수 있다. 클래스 탬플릿은 함수 템플릿보다 조금 더 복잡하지만 선언은 더 쉽다. 간단한 클래스를 만들어 보겠심다. 아래 소스 코드는 cpp파일에 넣지말고 block.h 따위의 해더 파일을 만들어서 따로 include하기 바랍니다. 그리고 템플릿 클래스의 메소드는 아래처럼 몽땅 inline화 시켜야 합니다.
- template<typename T>
- class block {
- public:
- T &operator[](int n) {
- return arr[n];
- }
- T &operator[](int n) {
- private:
- T arr[10];
- public:
- };
종류는 알 수 없지만 아무튼 T라는 타입의 크기 10짜리 배열을 가지고 []연산자를 통해서 접근할 수 있는 클래스임다. 간단하게 배열을 클래스화 시키고 템플릿을 사용해서 어려 타입에 관한 버젼을 만들었심니다. 간단하고 잘 작동하는 훌륭한 클래스 인거심다. 함수 탬플릿과 마찬가지로 클래스 탬플릿도 전문화 할 수 있지만 여기서는 다루지 않겠심다. 이렇게 만든 템플릿 클래스는 다음과 같이 사용합니다.
- int main(void)
- {
- using namespace std;
- block<int> arr;
- arr[3] = 5;
- cout << arr[3] << endl;
- return 0;
- }
클래스의 이름을 쓰고 <>안에 타입의 이름을 써주면 템플릿 클래스를 인스턴스화 할 수 있습니다. 아래처럼 한 템플릿에 여러가지 타입을 사용할 수도 있습니다.
- template<typename T, typename K>
- ...어쩌구 저쩌구...
이런 경우에도 선언시 <>안에 타입의 이름을 적어주면 됨니다.
- block<int, char> arr;
#비타입 인수
템플릿의 인수는 타입만이 가능한것이 아니라 비 타입인자(상수)나 다른 템플릿도 가능합니다. 비 타입인자를 사용해 우리의 block 클래스를 더 좋게 만들어 보겠습니다.
- template<typename T, int n>
- struct block {
- T& operator[](int n) {
- return arr[n];
- }
- T arr[n];
- T& operator[](int n) {
- };
block를 클래스가 아닌 구조체로 선언하는 트릭을 사용해서 block을 인스턴스화 할때 배열처럼 {}를 사용한 초기화를 가능하도록 만들었습니다. 아래와 같이 사용하면 됨니다.
- int main(void)
- {
- using namespace std;
- block<int ,10> arr;
- block<block<int, 10>, 10> arr2;
- block<char, 5> chr = {'a', 'b', 'c', 'd', 'e'};
- arr[5] = 5;
- arr[5][5] = 55;
- cout << arr[5] << endl;
- cout << arr2[5][5] << endl;
- cout << chr[3] << endl;
- return 0;
- }
간단한 트릭으로 block와 배열의 차이점이 없어져 버렸습니다. 오히려 block은 각종 메소드를 추가하면 더욱 강력하게 만들 수 있기 때문에 이제 배열의 시대는 끝난것 처럼 보입니다.
#템플릿의 인스턴스화
마지막으로 템플릿 함수와 클래스가 언제 인스턴스화 되는지를 알아보겠심다.
간단하게 말해서 템플릿은 컴파일이 거의 끝나가는 시점에서 기계어를 만들어내기 직전에 인스턴스화 됨니다. 따라서 위에서 살펴봤던 비 타입인자를 사용할때 소스코드안에 다음과 같은 구문이 있다면
- block<int, 5> arr;
- block<int, 10> arr2;
int arr[5]와 int arr[10]을 가지는 block이 컴파일 타임에 만들어 지게 됨니다. 위에서 비 타입인자를 설명할때 '비 타이인자(상수) ' 라고 언급한건 이와 같은 특징 때문입니다. 템플릿에 사용된 비 타입인자들은 모두 컴파일 타임에 결정되기 때문에 상수이외에는 사용할 수 없는 거심다. 바꿔 말하면 비 타입인자를 사용하면 컴파일 타임에 모든것이 결정되기 때문에 고속 라이브러리를 만들 수 있다고 하는데....해보질 못해서 모르겠심다 -_-;
아무튼 이러한 특징 때문에 block는 동적할당을 할 수가 없습니다. 물론 동적할당이 가능하도록 수정할 수는 있지만 그렇게 되면 {} 초기화를 사용할 수 없습니다. 배열은 배열대로 나름 쓸때가 있는 거심다. 안타깝지만 block 클래스는 이대로 내버려 두고 1장을 마침니다.
(1)
(
