본문 바로가기

Windows Developer/C++

[C++]템플릿(template)

템플릿은 두 가지가 있습니다.
  • 함수 템플릿 : 함수를 만들어 내는 메타 함수
  • 클래스 템플릿 :  클래스를 만들어 내는 메타 클래스

 

1, 함수 템플릿

 함수 템플릿은 함수를 만들기 위한 메타 함수( 함수의 틀)입니다.

 

함수는 어떤 기능을 수행하는 연산들의 집합입니다.

만약 출력 기능을 하는 함수가 있다면 이 함수의 기능은 출력입니다. 이때 지금까지 공부한 함수는 출력할 데이터(정수, 실수, 문자, 문자열 등)가 무엇인지에 따라 여러개의 함수를 작성해야만 했습니다.

함수 템플릿을 사용하면 메타 함수만 만들어 놓고 출력할 데이터의 타입을 컴파일 시간에 결정하여 여러 함수를 자동으로 생성할 수 있습니다.

 

예를 들어 설명하겠습니다. 정수, 실수, 문자열을 출력하는 예제입니다.

#include <iostream>
using namespace std;
void Print(int n)
{
    cout << n << endl;
}
void Print(double d)
{
    cout << d << endl;
}
void Print(const char* s)
{
    cout << s << endl;
}
void main( )
{
    Print( 10 );
    Print( 1.414 );
    Print( "Hello!" );
}
  1. 10
    1.414
    Hello!

 이렇게 정수, 실수, 문자열을 출력하는 함수를 각각 오버로딩하여 만들어야 합니다. 또 다른 데이터의 타입이 늘어난다면 출력함수도 계속 늘어나야 하는 문제가 발생합니다.

 

이와 같은 문제를 깔끔하게 해결하는 방법은 함수 템플릿을 사용하는 것입니다.

아래와 같은 함수를

#include <iostream>
using namespace std;
void Print(int n)
{
    cout << n << endl;
}
void main( )
{
    Print( 10 );
}
  1. 10

 아래와 같이 작성할 수 있습니다.

 함수의 데이터 타입을 결정하지 않고 템플릿 함수로 작성합니다. 

컴파일러에 의해 데이터 타입이 결정되고 함수가 생성됩니다.

#include <iostream>
using namespace std;
template <typename T>
void Print(T n)
{
    cout << n << endl;
}
void main( )
{
    Print( 10 );// 10이 정수 타입이므로 T가 int로 결정됩니다.
}
  1. 10

 10이 정수이므로 T가 int로 결정되어 함수가 호출됩니다.

 

그래서 아래와 같은 모든 코드가 가능합니다.

#include <iostream>
using namespace std;
template <typename T>
void Print(T data)
{
    cout << data << endl;
}
void main( )
{
    Print( 10 );
    Print( 1.414 );
    Print( "Hello!" );
}
  1. 10
    1.414
    Hello!

 사용법은 간단합니다.

 

컴파일러가 수행하는 절차를 그림으로 설명하면 아래와 같습니다.


위 예제는 아래와 같이 실제 함수로 명시적으로 호출할 수 있습니다.

#include <iostream>
using namespace std;
template <typename T>
void Print(T n)
{
    cout << n << endl;
}
void main( )
{
    Print<int>( 10 );
    Print<double>( 1.414 );
    Print<const char*>( "Hello!" );
}
  1. 10
    1.414
    Hello!

 Print() 템플릿 함수의 실제함수는 Print<int>( ), Print<double>( ), Print<const char*>( )입니다.

 

마지막 예로 Swap() 함수를 템플릿으로 작성하도록 하겠습니다.

template <typename T>
void Swap(T& argL, T& argR)
{
    T temp = argL;
    argL = argR;
    argR = temp;
}
void main( )
{
    int n1 = 10, n2 = 20;
    double d1 = 1.5, d2 = 3.8;

    Swap( n1, n2);
    Swap( d1, d2);

    cout << n1 << ',' << n2 << endl;
    cout << d1 << ',' << d2 << endl;
}
  1. 20,10
    3.8,1.5

  Swap() 함수처럼 기능은 같고 여러 데이터 타입이 가능하도록 기능을 하는 함수들을 만들때 사용합니다.

 

2, 클래스 템플릿

 클래스 템플릿은 클래스를 만들기 위한 메타 클래스( 클래스의 틀)입니다.

 

클래스는 객체들의 공통된 개념 정의입니다.

클래스의 개념과 정의는 바뀌지 않으나 클래스가 사용하는 데이터 타입이 여러 종류라면 모든 테이터 타입이 가능하도록 클래스를 하나하나 만들어 사용해야 합니다.

클래스 템플릿을 사용하면 메타 클래스만 만들어 놓고 클래스의 데이터의 타입을 컴파일 시간에 결정하여 여러 클래스를 자동으로 생성할 수 있습니다.

 

예를 들어 int 형의 x, y와 double 형의 x, y를 사용하는 Point 클래스가 모두 필요하다면 아래와 같이 두 클래스를 정의해야 합니다.

class PointInt
{
    int x;
    int y;
public:
    PointInt(int _x =0 , int _y =0 ):x(_x),y(_y) { }
    void Print( )const { cout << x <<',' << y << endl; }
};
class PointDouble
{
    double x;
    double y;
public:
    PointDouble(double _x =0 , double _y =0 ):x(_x),y(_y) { }
    void Print( )const { cout << x <<',' << y << endl; }
};
void main( )
{
    PointInt ptInt(2,3);
    ptInt.Print();

    PointDouble ptDouble(1.1, 2.2);
    ptDouble.Print();
}
  1. 2,3
    1.1,2.2

정수를 저장하기 위한 클래스와 실수를 저장하기 위한 클래스가 각각 정의되어 있습니다.

 

하지만 클래스 템플릿을 사용하면 컴파일러의 의해 자동으로 생성시킬 수 있습니다.

template <typename T>
class Point
{
    T x;
    T y;
public:
    Point(T _x =0 , T _y =0 ):x(_x),y(_y) { }
    void Print( )const { cout << x <<',' << y << endl; }
};
void main( )
{
    Point<int> ptInt(2, 3);
    ptInt.Print( );

    Point<double> ptDouble(1.1, 2.2);
    ptDouble.Print( );
}
  1. 2,3
    1.1,2.2

 T는 컴파일러의 의해 int와 double로 치환되어 두 클래스(Point<int> 와 Point<double>)가 생성됩니다.

위 예제를 컴파일러가 수행하는 절차를 그림으로 보이면 아래와 같습니다.


간단한 스택 클래스를 템플릿으로 작성하도록 하겠습니다. 자료구조를 추상화한 클래스들은 여러 종류의 데이터를 취급하므로 보통 클래스 템플릿으로 만들어 사용합니다.

우리는 클래스 템플릿을 공부하기 위해 단순한(에러처리 없고 주요 코드만 있는) 스택 클래스를 만들어 보겠습니다.

 

우선 정수만 저장 가능한 스택 클래스를 작성합니다.

 #include <iostream>
using namespace std;

class Stack
{
    int *buf;
    int top;
    int size;
public:
    Stack(int sz = 100):buf(0),top(0),size(sz) { buf = new int[size]; }
    ~Stack( ) { delete [] buf; }
    void Push(int data) {   buf[top++] = data; }
    int Pop( ) { return buf[--top]; }
};
void main( )
{
    Stack iStack;

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

    cout << iStack.Pop() << endl;
    cout << iStack.Pop() << endl;
    cout << iStack.Pop() << endl;
}
  1. 30
  2. 20
  3. 10

정수만 저장 가능한 스택입니다.

 

이제 위 스택 클래스를  템플릿 클래스로 작성합니다.

#include <iostream>
#include <string>
using namespace std;
template <typename T>
class Stack
{
    T *buf;
    int top;
    int size;
public:
    Stack(int sz = 100):buf(0),top(0),size(sz) { buf = new T[size]; }
    ~Stack( ) { delete [] buf; }
    void Push(T data) {  buf[top++] = data; }
    T Pop( ) { return buf[--top]; }
};
void main( )
{
    Stack<int> iStack;
    iStack.Push( 10 );
    iStack.Push( 20 );
    iStack.Push( 30 );
    cout << iStack.Pop() << endl;
    cout << iStack.Pop() << endl;
    cout << iStack.Pop() << endl;

    Stack<double> dStack;
    dStack.Push( 1.1 );
    dStack.Push( 2.1 );
    dStack.Push( 3.3 );
    cout << dStack.Pop() << endl;
    cout << dStack.Pop() << endl;
    cout << dStack.Pop() << endl;

    Stack<string> sStack;
    sStack.Push( "ABC" );
    sStack.Push( "def" );
    sStack.Push( "GHIJK" );
    cout << sStack.Pop() << endl;
    cout << sStack.Pop() << endl;
    cout << sStack.Pop() << endl;
}
  1. 30
    20
    10
    3.3
    2.1
    1.1
    GHIJK
    def
    ABC

 템플릿으로 만들어 놓으면 언제든지 필요한 자료형 스택 클래스를 사용할 수 있습니다.


'Windows Developer > C++' 카테고리의 다른 글

[C++] 형변환 연산자  (0) 2010.07.26
[C++] 가상함수  (0) 2010.07.26
[C++]STL 표준 C++ 라이브러리  (0) 2010.07.20
[C++]상수 함수  (1) 2010.07.19
[C++]객체지향 프로그램의 4대 특징  (0) 2010.07.17