[디딤돌 C++] 60. 템플릿 클래스

이번에는 템플릿 클래스를 알아볼게요.

템플릿 클래스는 멤버 필드의 형식이나 일부 멤버 메서드의 인수의 형식만 다르고 메서드 내부의 논리 전개가 같을 때 사용합니다. 템플릿 클래스도 템플릿 함수처럼 실제 클래스를 만들기 위한 틀일 뿐입니다. 템플릿 클래스를 정의할 때도 템플릿 형식 인자를 표현하는 tempate <typename 타입명,…> 부분이 필요합니다.

template <typename 타입명,…>

class 클래스명

{

};

사용하는 코드에서 템플릿 형식 인자를 구체적으로 결정하며 이에 맞게 컴파일러가 실제 클래스의 코드를 템플릿 클래스를 참고하여 만듭니다. 이러한 특징으로 템플릿 클래스의 멤버 메서드 구현 코드도 헤더 파일에 작성하세요.

클래스명<타입명> 변수;

다음은 템플릿 클래스 문법을 파악하기 위한 간단한 예를 두 가지 형태로 보여 드리겠습니다.

첫 번째는 멤버 메서드를 템플릿 클래스 외부에 구현하고 두 번째는 멤버 메서드를 템플릿 클래스 내부에 구현하는 것입니다. 일반적으로 템플릿 클래스의 멤버 메서드를 템플릿 클래스 내부에 만듭니다.

먼저 멤버 메서드를 템플릿 클래스 외부에 구현하는 방법입니다. 템플릿 클래스의 멤버 메서드를 구현할 때도 템플릿 형식 인자를 표현하는 template <typename 타입명,…> 부분이 필요합니다. 그리고 스코프 앞에 클래스명과 함께 템플릿 형식 인자를 표현해야 합니다.

template <typename 타입명,…>

리턴 형식 클래스명<타입명,…>::메서드명(입력 매개 변수 목록)

{

}

다음은 인덱스 연산이 가능한 동적 배열을 템플릿 클래스로 정의한 것입니다. 보관할 데이터 형식을 data 이름으로 가정할게요.

template <typename data>
class EHArray
{
데이터를 보관할 메모리를 동적으로 할당할 것이며 이를 기억하기 위한 멤버 필드와 크기를 기억할 멤버를 추가하세요.
    data *base;
    const size_t bsize;
public:
사용하는 곳에서 생성할 때 동적 배열의 크기를 전달받기로 합시다. 
    EHArray(size_t bsize);
배열을 동적으로 생성할 것이므로 소멸자에서 이를 해제해야겠죠.
    ~EHArray();
인덱스 연산자를 중복 정의하여 사용자 편의를 제공합시다.
    data &operator[](size_t index);
};

템플릿 클래스 외부에 멤버 메서드를 정의할 때는 모든 메서드마다 template <typename 타입명,…>을 표현해야 합니다. 그리고 스코프 연산자 앞에는 클래스명<타입명,…>으로 표현합니다.

template <typename data>
EHArray<data>::EHArray(size_t bsize):bsize(bsize)
{
동적 배열의 메모리를 할당하고 모든 원소의 값을 0으로 초기화할게요.
    base = new data[bsize];
    for(int i = 0; i<bsize;++i)
    {
        base[i]=0;
    }
}

모든 메서드마다 template <typename 타입명,…>을 표현해야 합니다.

template <typename data>
EHArray<data>::~EHArray()
{
동적으로 생성한 메모리를 해제합니다.
    delete[] base;
}

모든 메서드마다 template <typename 타입명,…>을 표현해야 합니다.

template <typename data>
data &EHArray<data>::operator[](size_t index)
{
유효한 인덱스일 때 저장소에 해당 인덱스의 원소를 반환합니다.
    if((index>=0)&&(index<bsize))
    {
        return base[index];
    }
유효하지 않으면 예외를 던져줍니다.
    throw "인덱스가 범위를 벗어났습니다.";
}

다음은 템플릿 클래스 내부에 멤버 메서드를 구현한 예입니다.

class EHArray
{
    data *base;
    const size_t bsize;
public:
    EHArray(size_t bsize):bsize(bsize)
    {
        base = new data[bsize];
        for(int i = 0; i<bsize;++i)
        {
            base[i]=0;
        }
    }
    ~EHArray()
    {
        delete[] base;
    }
    data &operator[](size_t index)
    {
        if((index>=0)&&(index<bsize))
        {
            return base[index];
        }
        throw "인덱스가 범위를 벗어났습니다.";
    }
};

이와 같이 정의한 템플릿 클래스를 사용할 때는 템플릿 형식 인자를 구체적으로 결정하여 표현해야 합니다. 예를 들어 정수를 보관하는 EHArrray 형식 변수를 선언할 때는 다음과 같이 나타낼 수 있습니다.

EHArray<int> earr1(10);//크기가 10인 배열(템플릿 형식:int) 선언
EHArray<int> *earr2 = new EHArray<int>(10);//크기가 10인 배열(템플릿 형식:int) 동적 생성

이처럼 템플릿 클래스를 사용하는 방법은 형식 변수를 선언하거나 동적 생성하는 부분만 차이가 있을 뿐 나머지 부분은 똑같습니다.

다음은 앞에서 예를 들었던 동적 배열을 템플릿 클래스로 정의하고 이를 사용하는 예제 코드입니다.

//템플릿 클래스 내부에 멤버 메서드 구현
//EHArray.h
#pragma once
template <typename data>
class EHArray
{
    data *base;
    const size_t bsize;
public:
    EHArray(size_t bsize):bsize(bsize)
    {
        base = new data[bsize];
        for(int i = 0; i<bsize;++i)
        {
            base[i]=0;
        }
    }

    ~EHArray()
    {
        delete[] base;
    }

    data &operator[](size_t index)
    {
        if((index>=0)&&(index<bsize))
        {
            return base[index];
        }
        throw "인덱스가 범위를 벗어났습니다.";
    }
};
//Program.cpp
#include <iostream>
using namespace std;

#include "EHArray.h"
typedef EHArray<int> IntArray; //템플릭 형식 int인 EHArray를 IntArray로 타입명 지정

int main()
{
    IntArray arr(10); //크기가 10인 IntArray 선언

    arr[2]=9;
    arr[4] = 20;
    for(int i = 0; i<10;i++)
    {
        cout<<arr[i]<<" ";
    }
    cout<<endl;
    return 0;
}

▷ 실행 결과

0 0 9 0 20 0 0 0 0 0