클래스 템플릿 기초

2025. 3. 28. 08:47Programming Languages/C++

7.2 클래스 템플릿 기초

7.2.1 클래스 템플릿의 개념

클래스 템플릿은 다양한 데이터 타입에 대해 동일한 클래스 구조를 제공합니다:

#include <iostream>

// 클래스 템플릿 정의
template <typename T>
class Box {
private:
    T content;
    
public:
    // 생성자
    Box(const T& item) : content(item) {}
    
    // 내용물 설정
    void setContent(const T& item) {
        content = item;
    }
    
    // 내용물 반환
    T getContent() const {
        return content;
    }
    
    // 내용물 출력
    void printContent() const {
        std::cout << "Box 내용물: " << content << std::endl;
    }
};

int main() {
    // int 타입의 Box
    Box<int> intBox(42);
    intBox.printContent();
    
    // double 타입의 Box
    Box<double> doubleBox(3.14159);
    doubleBox.printContent();
    
    // string 타입의 Box
    Box<std::string> stringBox("Hello, Templates!");
    stringBox.printContent();
    
    // 내용물 변경
    intBox.setContent(100);
    intBox.printContent();
    
    return 0;
}

클래스 템플릿을 사용할 때는 항상 템플릿 인수를 명시적으로 지정해야 합니다(예: Box<int>, Box<double> 등).

7.2.2 멤버 함수 정의

클래스 템플릿의 멤버 함수는 클래스 내부 또는 외부에 정의할 수 있습니다:

#include <iostream>

// 클래스 템플릿 선언
template <typename T>
class Array {
private:
    T* elements;
    int size;
    
public:
    // 생성자 선언
    Array(int s);
    
    // 소멸자 선언
    ~Array();
    
    // 요소 접근 함수 선언
    T& operator[](int index);
    
    // 배열 크기 반환 함수 선언
    int getSize() const;
};

// 클래스 외부에서 생성자 정의
template <typename T>
Array<T>::Array(int s) : size(s) {
    elements = new T[size];
    for (int i = 0; i < size; i++) {
        elements[i] = T();  // 기본값으로 초기화
    }
}

// 소멸자 정의
template <typename T>
Array<T>::~Array() {
    delete[] elements;
}

// 연산자 오버로딩 정의
template <typename T>
T& Array<T>::operator[](int index) {
    if (index < 0 || index >= size) {
        throw std::out_of_range("인덱스가 범위를 벗어났습니다.");
    }
    return elements[index];
}

// 크기 반환 함수 정의
template <typename T>
int Array<T>::getSize() const {
    return size;
}

int main() {
    // int 배열 생성
    Array<int> intArray(5);
    
    // 배열 요소 설정
    for (int i = 0; i < intArray.getSize(); i++) {
        intArray[i] = i * 10;
    }
    
    // 배열 요소 출력
    std::cout << "int 배열 내용:" << std::endl;
    for (int i = 0; i < intArray.getSize(); i++) {
        std::cout << "intArray[" << i << "] = " << intArray[i] << std::endl;
    }
    
    // double 배열 생성
    Array<double> doubleArray(3);
    
    doubleArray[0] = 3.14;
    doubleArray[1] = 2.71;
    doubleArray[2] = 1.41;
    
    // 배열 요소 출력
    std::cout << "\ndouble 배열 내용:" << std::endl;
    for (int i = 0; i < doubleArray.getSize(); i++) {
        std::cout << "doubleArray[" << i << "] = " << doubleArray[i] << std::endl;
    }
    
    try {
        // 범위를 벗어난 접근
        std::cout << intArray[10] << std::endl;
    } catch (const std::out_of_range& e) {
        std::cerr << "예외 발생: " << e.what() << std::endl;
    }
    
    return 0;
}

클래스 템플릿의 멤버 함수를 클래스 외부에 정의할 때는 항상 template <typename T> 접두사와 함께 클래스 이름에 템플릿 매개변수를 명시해야 합니다(Array<T>::함수이름).

7.2.3 클래스 템플릿 특수화

특정 타입에 대해 클래스 템플릿을 다르게 구현해야 할 때 특수화를 사용합니다:

#include <iostream>
#include <string>

// 일반 클래스 템플릿
template <typename T>
class DataHolder {
private:
    T data;
    
public:
    DataHolder(const T& value) : data(value) {}
    
    void display() const {
        std::cout << "일반 데이터: " << data << std::endl;
    }
    
    T getValue() const {
        return data;
    }
};

// char* 타입에 대한 특수화
template <>
class DataHolder<char*> {
private:
    char* data;
    
public:
    DataHolder(char* value) {
        // 문자열 복사 (깊은 복사)
        size_t len = strlen(value) + 1;
        data = new char[len];
        strcpy(data, value);
    }
    
    ~DataHolder() {
        delete[] data;
    }
    
    void display() const {
        std::cout << "문자열 데이터: " << data << " (길이: " << strlen(data) << ")" << std::endl;
    }
    
    const char* getValue() const {
        return data;
    }
};

// std::string 타입에 대한 특수화
template <>
class DataHolder<std::string> {
private:
    std::string data;
    
public:
    DataHolder(const std::string& value) : data(value) {}
    
    void display() const {
        std::cout << "std::string 데이터: " << data << " (길이: " << data.length() << ")" << std::endl;
    }
    
    std::string getValue() const {
        return data;
    }
    
    // 추가 기능
    void toUpperCase() {
        for (size_t i = 0; i < data.length(); i++) {
            data[i] = std::toupper(data[i]);
        }
    }
};

int main() {
    // 일반 템플릿 사용
    DataHolder<int> intHolder(42);
    intHolder.display();
    
    // char* 특수화 사용
    char text[] = "Hello, World";
    DataHolder<char*> charHolder(text);
    charHolder.display();
    
    // std::string 특수화 사용
    DataHolder<std::string> stringHolder("C++ Templates");
    stringHolder.display();
    
    // std::string 특수화의 추가 기능 사용
    stringHolder.toUpperCase();
    stringHolder.display();
    
    return 0;
}

클래스 템플릿 특수화를 통해 특정 타입에 맞는 최적화나 특별한 동작을 구현할 수 있습니다.

7.2.4 부분 특수화

클래스 템플릿의 부분 특수화는 템플릿 매개변수의 일부만 특수화하는 것입니다:

#include <iostream>

// 기본 템플릿 (두 개의 타입 매개변수)
template <typename T, typename U>
class Pair {
private:
    T first;
    U second;
    
public:
    Pair(const T& f, const U& s) : first(f), second(s) {}
    
    void display() const {
        std::cout << "일반 템플릿 - 첫 번째: " << first << ", 두 번째: " << second << std::endl;
    }
    
    T getFirst() const { return first; }
    U getSecond() const { return second; }
};

// T와 U가 같은 타입일 때의 부분 특수화
template <typename T>
class Pair<T, T> {
private:
    T first;
    T second;
    
public:
    Pair(const T& f, const T& s) : first(f), second(s) {}
    
    void display() const {
        std::cout << "동일 타입 특수화 - 첫 번째: " << first << ", 두 번째: " << second << std::endl;
    }
    
    T getFirst() const { return first; }
    T getSecond() const { return second; }
    
    // 두 값이 같은지 비교하는 추가 기능
    bool areEqual() const {
        return first == second;
    }
};

// 포인터 타입에 대한 부분 특수화
template <typename T, typename U>
class Pair<T*, U*> {
private:
    T* first;
    U* second;
    
public:
    Pair(T* f, U* s) : first(f), second(s) {}
    
    void display() const {
        std::cout << "포인터 특수화 - 첫 번째: " << *first << ", 두 번째: " << *second << std::endl;
    }
    
    T* getFirst() const { return first; }
    U* getSecond() const { return second; }
};

int main() {
    // 일반 템플릿 사용
    Pair<int, double> p1(42, 3.14);
    p1.display();
    
    // 동일 타입 특수화 사용
    Pair<int, int> p2(10, 20);
    p2.display();
    std::cout << "두 값이 같나요? " << (p2.areEqual() ? "예" : "아니오") << std::endl;
    
    // 포인터 특수화 사용
    int x = 5, y = 10;
    Pair<int, int> p3(&x, &y);
    p3.display();
    
    return 0;
}

부분 특수화는 보다 구체적인 타입 패턴에 대해 최적화된 구현을 제공할 때 유용합니다.

7.2.5 템플릿 매개변수의 기본값

템플릿 매개변수에도 기본값을 지정할 수 있습니다:

#include <iostream>

// 기본 타입 매개변수를 가진 클래스 템플릿
template <typename T = int, int Size = 10>
class SimpleArray {
private:
    T data[Size];
    
public:
    SimpleArray() {
        for (int i = 0; i < Size; i++) {
            data[i] = T();  // 기본값으로 초기화
        }
    }
    
    void setAt(int index, const T& value) {
        if (index >= 0 && index < Size) {
            data[index] = value;
        }
    }
    
    T getAt(int index) const {
        if (index >= 0 && index < Size) {
            return data[index];
        }
        return T();
    }
    
    int getSize() const {
        return Size;
    }
};

int main() {
    // 기본 매개변수 사용 (int 타입, 크기 10)
    SimpleArray<> array1;
    for (int i = 0; i < array1.getSize(); i++) {
        array1.setAt(i, i * 10);
    }
    
    std::cout << "array1 내용 (기본 매개변수):" << std::endl;
    for (int i = 0; i < array1.getSize(); i++) {
        std::cout << array1.getAt(i) << " ";
    }
    std::cout << std::endl;
    
    // 타입만 지정 (double 타입, 크기 10)
    SimpleArray<double> array2;
    for (int i = 0; i < array2.getSize(); i++) {
        array2.setAt(i, i * 1.1);
    }
    
    std::cout << "array2 내용 (double 타입):" << std::endl;
    for (int i = 0; i < array2.getSize(); i++) {
        std::cout << array2.getAt(i) << " ";
    }
    std::cout << std::endl;
    
    // 모든 매개변수 지정 (char 타입, 크기 5)
    SimpleArray<char, 5> array3;
    array3.setAt(0, 'H');
    array3.setAt(1, 'e');
    array3.setAt(2, 'l');
    array3.setAt(3, 'l');
    array3.setAt(4, 'o');
    
    std::cout << "array3 내용 (char 타입, 크기 5):" << std::endl;
    for (int i = 0; i < array3.getSize(); i++) {
        std::cout << array3.getAt(i);
    }
    std::cout << std::endl;
    
    return 0;
}

템플릿 매개변수의 기본값을 사용하면 사용자가 모든 템플릿 인수를 명시적으로 지정하지 않아도 됩니다.

 

'Programming Languages > C++' 카테고리의 다른 글

가변 템플릿(Variadic Templates) (C++11)  (0) 2025.03.28
비타입 템플릿 매개변수  (0) 2025.03.28
템플릿  (0) 2025.03.28
챕터6. 실습 문제  (0) 2025.03.28
고급 포인터 주제  (0) 2025.03.28