Menu
CoddyTech

클래스 템플릿

Coddy C++ 여정의 객체 지향 프로그래밍 섹션에 포함된 레슨. 104개 중 65번째.

함수 템플릿을 사용하면 타입에 독립적인 함수를 작성할 수 있는 것처럼, 클래스 템플릿을 사용하면 타입에 독립적인 클래스를 만들 수 있습니다. std::vector와 같은 컨테이너는 이렇게 동작합니다. 구조를 한 번 정의하면 컴파일러가 사용하는 각 타입에 맞는 버전을 생성합니다.

template <typename T>
class Box {
    T value;
public:
    Box(T v) : value(v) {}
    T getValue() const { return value; }
    void setValue(T v) { value = v; }
};

int main() {
    Box<int> intBox(42);
    Box<std::string> strBox("Hello");
    
    std::cout << intBox.getValue() << std::endl;  // 42
    std::cout << strBox.getValue() << std::endl;  // 안녕하세요
}

클래스 템플릿에서 객체를 생성할 때는 보통 꺾쇠괄호 안에 타입을 지정합니다. C++17부터는 컴파일러가 생성자 인수에서 타입을 추론할 수도 있습니다(Box b(42);는 Box<int>를 생성합니다). 하지만 추론할 인수가 없으면 타입을 직접 써야 합니다. 그러면 컴파일러가 해당 타입에 맞는 완전한 클래스 정의를 생성합니다.

클래스 템플릿에는 여러 개의 타입 매개변수와 정수와 같은 비타입 매개변수도 포함할 수 있습니다.

template <typename T, int Size>
class FixedArray {
    T data[Size];
public:
    T& operator[](int index) { return data[index]; }
    int size() const { return Size; }
};

FixedArray<double, 5> arr;  // double 값 5개로 이루어진 배열
arr[0] = 3.14;

클래스 외부에서 멤버 함수를 정의할 때는 템플릿 선언을 반복해야 합니다.

template <typename T>
class Container {
    T* data;
public:
    Container();
    ~Container();
};

template <typename T>
Container<T>::Container() : data(nullptr) {}

template <typename T>
Container<T>::~Container() { delete data; }

클래스 템플릿은 C++ 제네릭 프로그래밍의 토대이며, 어떤 타입에도 사용할 수 있는 재사용 가능한 자료 구조를 작성할 수 있게 해 줍니다.

challenge icon

챌린지

쉬움

클래스 템플릿을 사용해 모든 유형의 데이터를 담을 수 있는 유연한 컨테이너인 범용 저장 시스템을 만들어 봅시다. 템플릿 클래스를 헤더 파일에 정의하고, 메인 프로그램에서 다양한 유형으로 활용할 수 있음을 보여 주세요.

두 개의 파일을 만듭니다.

  • Storage.h: 유용한 몇 가지 연산을 제공하며 값 하나를 담는 간단한 컨테이너 역할을 하는 Storage라는 클래스 템플릿을 정의합니다.

    Storage 템플릿에는 다음이 있어야 합니다.

    • 저장된 값을 담는 private 멤버
    • 초깃값을 받는 생성자
    • 저장된 값을 반환하는 getValue() 메서드
    • 저장된 값을 업데이트하는 setValue() 메서드
    • 값이 유형 T의 기본 생성 값과 같으면 true, 그렇지 않으면 false를 반환하는 isEmpty() 메서드

    또한 두 개의 유형 매개변수를 받으며 서로 관련된 값 두 개를 저장하는 Pair라는 클래스 템플릿도 만듭니다.

    • 첫 번째 값과 두 번째 값을 저장하는 private 멤버(서로 다른 유형일 수도 있음)
    • 두 값을 모두 초기화하는 생성자
    • 각 값을 가져오는 getFirst() 및 getSecond() 메서드
    • (<first>, <second>)를 출력하는 display() 메서드
  • main.cpp: 네 개의 입력을 읽습니다(각각 별도의 줄에 입력).
    1. 정수 값
    2. 새 정수 값
    3. 문자열 값
    4. 실수 값

    다음과 같이 템플릿을 사용해 보세요.

    1. 첫 번째 정수로 Storage<int>를 만들고 Int storage: <value>를 출력합니다.
    2. setValue()를 사용해 두 번째 정수로 값을 업데이트한 다음 Updated: <value>를 출력합니다.
    3. 문자열 입력으로 Storage<std::string>을 만들고 String storage: <value>를 출력합니다.
    4. 문자열 저장소가 비어 있는지 확인하고 Is empty: <true/false>를 출력합니다(true 또는 false를 출력).
    5. 문자열 값과 실수 값으로 Pair<std::string, double>을 만든 다음 display()를 호출합니다.
    6. 두 정수 입력으로 Pair<int, int>를 만든 다음 display()를 호출합니다.

예를 들어 입력이 42, 100, Hello, 3.14라면 다음과 같습니다.

Int storage: 42
Updated: 100
String storage: Hello
Is empty: false
(Hello, 3.14)
(42, 100)

동일한 Storage 템플릿이 정수와 문자열 모두에서 매끄럽게 작동하고, Pair가 서로 다른 유형을 조합할 수 있다는 점에 주목하세요. 컴파일러는 사용한 각 유형 조합에 대해 별도의 클래스 정의를 생성합니다. 단일 유형 템플릿에는 template <typename T> 구문을, 여러 유형 매개변수를 사용하는 템플릿에는 template <typename T, typename U> 구문을 사용하세요.

직접 해보기

#include <iostream>
#include <string>
#include "Storage.h"

using namespace std;

int main() {
    // 입력값 읽기
    int intValue1;
    int intValue2;
    string strValue;
    double doubleValue;
    
    cin >> intValue1;
    cin >> intValue2;
    cin >> strValue;
    cin >> doubleValue;
    
    // TODO: 첫 번째 정수로 Storage<int>를 생성하고 출력: Int storage: <value>
    
    // TODO: setValue()를 사용해 두 번째 정수로 값을 갱신하고 출력: Updated: <value>
    
    // TODO: 문자열 입력값으로 Storage<std::string>을 생성하고 출력: String storage: <value>
    
    // TODO: 문자열 저장소가 비어 있는지 확인하고 출력: Is empty: true/false
    
    // TODO: 문자열과 double 값으로 Pair<std::string, double>을 생성하고 display() 호출
    
    // TODO: 두 정수로 Pair<int, int>를 생성하고 display() 호출
    
    return 0;
}
quiz icon실력 점검

이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.

객체 지향 프로그래밍의 모든 레슨

직접 연습해 보세요: 온라인 C++ 컴파일러