HOWTO · C++

C++ でのクラス テンプレートの継承

この記事は、C++ でクラス テンプレートを使用して継承を実装するためのガイドとして役立ちます。

このページの内容

この記事では、C++ で最も一般的で頻繁に使用されるメソッドの 1つ (つまり、クラス テンプレート) について説明します。

C++ へのテンプレートの追加は、ジェネリック プログラミングとして知られる新しいコーディング パラダイムをもたらしました。 これは現在、C++ プログラマーのツールキットの重要な要素であり、多くの標準ライブラリの基盤であり、新しい C++ ハッカーの多くがこれまで遭遇したことのないものです。

ジェネリック プログラミングは、オブジェクト指向プログラミングにおける継承の概念と対比されることがあります。 一方、真のマルチパラダイム アプローチでは、2つの相互作用に注目する必要があります。

C++ のテンプレート

C++ では、テンプレートは基本的ですが強力なツールです。 基本的な考え方は、データ型をパラメーターとして渡すことです。これにより、複数のデータ型に対して同じコードを記述する必要がなくなります。

たとえば、ソフトウェア ビジネスでは、多くのデータの種類に対して sort() が必要になる場合があります。 多数のコードを記述して管理する代わりに、単一の sort() 関数を作成して、データ型をパラメーターとして渡すことができます。

C++ では、テンプレートをサポートするために 2つの新しいキーワード template と typename が追加されました。 キーワード class は、2 番目のキーワードの代わりにいつでも使用できます。

構文:

template <typename T>
void myFunction(T var )
{
    //function definition
}

上記の構文では、テンプレート変数をパラメーターとして受け入れる関数を定義するために、行 template<typename T> を追加したことがわかります。 現在、この関数は、任意のデータ型のパラメーターを受け入れることができます。 これは、int、char、およびその他の多くのデータ型に使用される汎用関数です。

C++ でのテンプレートのしくみ

テンプレートは、コンパイル プロセス中に展開されます。 これはマクロに似ています。

一方、コンパイラはテンプレート展開の前に型チェックを実行します。 概念は簡単です。ソース コードには 1つの関数またはクラスのみが含まれますが、生成されたコードには同じ関数またはクラスの多数のコピーが含まれる場合があります。

これについては、以下の例で詳しく説明します。

#include <iostream>
using namespace std;

template <typename T>
T checkMaxValue(T a, T b) {
  if (a > b)
    return a;
  else if (b > a)
    return b;
  else
    return 0;
}

// driver program
int main() {
  cout << checkMaxValue(102, 204) << endl;  // int value
  cout << checkMaxValue('c', 'f') << endl;  // char value
}

ドライバー プログラムの最初の行で関数に 2つの整数を渡していることに注意してください。最終的には、両方の整数の最大値を返します。 2 行目で同じ関数に 2つの char 値を渡しました。関数は最大の char 値を返します。

コンパイル時に、以下に示すように関数を呼び出します。

int checkMaxValue(int a, int b)

2 番目の呼び出しでは、次のように記述します。

char checkMaxValue(char a, char b)

関数を呼び出すときに渡される変数の型に応じて、コンパイル時に同じ関数の複数のコピーが作成されることがわかります。

関数とクラスのテンプレートを使用できます。 両方のタイプと、両方を実装する方法について説明します。

C++ の関数テンプレート

さまざまなデータの種類を処理できる汎用関数を作成します。 Sort()、max()、min()、および printArray は関数テンプレートの例です。

コード例:

template <class T>
void sortArray(T arr[], int s) {
  for (int i = 0; i < s - 1; i++)
    for (int j = s - 1; i < j; j--)
      if (arr[j] < arr[j - 1]) {
        int var = arr[j];
        arr[j] = arr[j - 1];
        arr[j - 1] = var;
      }
  cout << " Sorted array : ";
  for (int i = 0; i < s; i++) cout << arr[i] << " ";
  cout << endl;
}

// Driver Code
int main() {
  int a[5] = {10, 50, 30, 40, 20};
  int n = 5;
  sortArray<int>(a, n);

  char c[5] = {'g', 'a', 'i', 'h', 'e'};
  sortArray<char>(c, n);
  return 0;
}

出力:

関数テンプレート出力

コード スニペットでは、配列のテンプレート タイプとその配列のサイズ値を受け入れ、並べ替えられた配列を出力できる関数 sortArray() を作成しました。 メイン コードで、int 配列と char 配列の 2つの異なる配列タイプに対して呼び出しました。

したがって、任意のデータ型を使用でき、同じ関数が機能します。 時間とスペースを節約し、ジェネリック プログラミングでも効率的です。

C++ のクラス テンプレート

関数テンプレートと同様に、クラス テンプレートは、クラスがデータ型に依存しないものを定義している場合に便利です。 LinkedList、BinaryTree、Stack、Queue、Array などのクラスは、クラス テンプレートの恩恵を受ける可能性があります。

構文:

template <class T>
class className {
 private:
  T a;
  ........public : T myFunc(T x);
  ........
};

この構文では、T はデータ型のプレースホルダーとして使用されるテンプレート型です。 このテンプレート クラスのオブジェクトを作成する場合、次の構文が使用されます。

className<dataType> objectName;

同様に、次の構文を使用して記述できます。

className<int> objectName;
className<double> objectName;

例:

#include <iostream>
using namespace std;

template <class T>
class Number {
 private:
  T numb;

 public:
  Number(T a) : numb(a) {}

  T get() { return numb; }
};

int main() {
  Number<int> integerNumber(50);
  Number<double> doubleNumber(8.9);
  cout << "integer Number = " << integerNumber.get() << endl;
  cout << "double Number = " << doubleNumber.get() << endl;
  return 0;
}

出力:

クラス テンプレート出力

この例では、同じクラスが整数と倍精度数を受け入れることができることがわかります。 この例は、クラス テンプレートの概念を理解できるようにするための非常に一般的なものです。

この概念は拡張可能で、LinkedList、Stack、Queue などのテンプレートを使用して多くのクラスを実装できます。

C++ でのクラス テンプレートの継承

テンプレート クラスからの継承は実行可能です。 すべての通常の継承とポリモーフィズムの規則が適用されます。

新しい派生クラスを汎用にする必要がある場合は、基本クラスに送信されるテンプレート引数を持つテンプレート クラスにする必要があります。 これは、継承はクラスでのみ可能であり、何らかのデータ型を渡すことによってインスタンス化されない限り、テンプレートはクラスではないためです。

構文:

template <class T>
class derived : public Base<T>{};

例:

#include <iostream>
using namespace std;

template <class T>
class Base {
 private:
  T val;

 public:
  void setVal(T a) { val = a; }
};
template <class T>
class Derived : public Base<T> {
 public:
  void setVal(T b) { Base<T>::setVal(b); }
};
int main() {
  Derived<int> a;
  a.setVal(4);
  return 0;
}

コード スニペットに、テンプレート型変数とメンバー関数を含む Base クラス テンプレートを作成しました。 さらに、クラス テンプレートでもあり、Base クラス テンプレートを拡張する派生クラスを作成しました。

Base クラスを拡張する場合、Base<T> のようなテンプレート変数を指定する必要があります。 Base クラスはジェネリックであり、派生クラスもジェネリックです。

派生クラスには、そのテンプレート パラメーターも含めることができます。 前の例は、次のように変更できます。

template <class T, class S>
class Derived : public Base<T> {
 private:
  S data;

 public:
  void setVal(T b) {
    Base<T>::setVal(b);
    data = b;
  }
};

したがって、クラス テンプレートを使用して継承を実装することもできます。 さらに、クラス テンプレートを使用してポリモーフィズムとアダプター クラスを実装できます。