Menu
Coddy logo textTech

クラステンプレート

CoddyのC++ジャーニー「オブジェクト指向プログラミング」セクションの一部 — レッスン 65/104。

関数テンプレートによって型に依存しない関数を記述できるのと同様に、クラステンプレートを使用すると、型に依存しないクラスを作成できます。これは 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;  // Hello
}

関数テンプレートとは異なり、クラステンプレートからオブジェクトを作成する際は、アングルブラケット(山括弧)を使用して型を明示的に指定する必要があります。その後、コンパイラはその特定の型に対応する完全なクラス定義を生成します。

クラステンプレートは、複数の型パラメータや、整数のような非型パラメータを持つこともできます:

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;  // 5つのdouble型の配列
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

チャレンジ

簡単

クラステンプレートを使用して、あらゆる種類のデータを保持できる柔軟なコンテナである汎用ストレージシステムを構築しましょう。テンプレートクラスをヘッダーファイルにまとめ、メインプログラムでさまざまな型に対する汎用性を実証します。

2つのファイルを作成します:

  • Storage.h: 単一の値を保持し、いくつかの便利な操作を行うシンプルなコンテナとして機能する、Storageという名前のクラステンプレートを定義します。

    Storageテンプレートには以下を含める必要があります:

    • 格納された値を保持するプライベートメンバ
    • 初期値を受け取るコンストラクタ
    • 格納された値を返すgetValue()メソッド
    • 格納された値を更新するsetValue()メソッド
    • 値が型Tのデフォルト構築された値と等しい場合にtrueを返し、そうでない場合にfalseを返すisEmpty()メソッド

    また、2つの関連する値を格納する、2つの型パラメータを持つPairという名前のクラステンプレートも作成します:

    • 1番目と2番目の値(異なる型の可能性があります)のためのプライベートメンバ
    • 両方の値を初期化するコンストラクタ
    • 各値を取得するためのgetFirst()およびgetSecond()メソッド
    • (<first>, <second>)と出力するdisplay()メソッド
  • main.cpp: 4つの入力(それぞれ別の行)を読み取ります:
    1. 整数の値
    2. 新しい整数の値
    3. 文字列の値
    4. double型の値

    以下の手順でテンプレートを実証してください:

    1. 最初の整数を使用してStorage<int>を作成し、Int storage: <value>と出力します。
    2. setValue()を使用して2番目の整数で更新し、Updated: <value>と出力します。
    3. 入力された文字列を使用してStorage<std::string>を作成し、String storage: <value>と出力します。
    4. 文字列ストレージが空かどうかを確認し、Is empty: <true/false>と出力します(trueまたはfalseを出力します)。
    5. 文字列とdouble型の値を使用してPair<std::string, double>を作成し、display()を呼び出します。
    6. 両方の整数入力を使用してPair<int, int>を作成し、display()を呼び出します。

例えば、入力が42100Hello3.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>構文を使用することを忘れないでください。

チートシート

クラステンプレートを使用すると、型に依存しないクラスを作成できます。コンパイラは、使用する型ごとに特定のバージョンを生成します。

基本的なクラステンプレート

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

クラステンプレートからオブジェクトを作成するときは、山括弧を使用して型を明示的に指定する必要があります。

Box<int> intBox(42);
Box<std::string> strBox("Hello");

複数の型パラメータ

クラステンプレートは、複数の型パラメータを持つことができます。

template <typename T, typename U>
class Pair {
    T first;
    U second;
public:
    Pair(T f, U s) : first(f), second(s) {}
};

非型パラメータ

テンプレートは、整数のような非型パラメータを受け取ることもできます。

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

FixedArray<double, 5> arr;  // 5つのdouble型の配列

クラス外でのメンバ関数の定義

クラス外でメンバ関数を定義する場合は、テンプレート宣言を繰り返します。

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; }

自分で試してみよう

#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: 1つ目の整数を使用して Storage<int> を作成し、「Int storage: <value>」と出力する
    
    // TODO: setValue() を使用して2つ目の整数で更新し、「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腕試し

このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。

オブジェクト指向プログラミングのすべてのレッスン