C++ サブクラスの継承

Namita Chaudhary 2023年10月12日
  1. C++ サブクラスの継承
  2. C++ のサブクラスでの多重継承
  3. C++ のサブクラスでの階層的継承
  4. C++ の継承におけるダイヤモンド問題
  5. まとめ
C++ サブクラスの継承

継承は、クラスが別のクラスのプロパティと動作を取得する OOP の機能です。 別のクラスを継承するクラスはサブクラスと呼ばれ、プロパティが継承されるクラスは基底クラスと呼ばれます。

この記事では、クラスの継承中に発生するひし形の問題について説明します。

C++ サブクラスの継承

継承は、あるクラスが別のクラスのプロパティを継承できるようにする機能です。 別のクラスのプロパティを継承するクラスはサブクラスまたは派生クラスと呼ばれ、プロパティが継承されるクラスは基本クラスまたは親クラスと呼ばれます。

継承の実際の例は、親のプロパティを継承する子です。

C++ の継承には、大きく分けて、単一継承、多重継承、多レベル継承、階層継承、ハイブリッド継承の 5 種類があります。

  1. 単一継承 - このタイプの継承では、単一のサブクラスが単一の基本クラスから継承されます。
  2. 多重継承 - このタイプの継承では、サブクラスは複数の基本クラスから継承されます。
  3. 階層的継承 - このタイプの継承では、複数のサブクラスが同じ基本クラスから継承されます。
  4. マルチレベル継承 - このタイプの継承では、あるクラスが別のクラスに継承され、そのクラスが別のクラスにも継承されます。 たとえば、AB、および C という 3つのクラスがあり、クラス C はクラス B によって継承され、クラス B はクラス A によって継承されます。
  5. ハイブリッド継承 - このタイプの継承では、複数のタイプの継承が組み合わされます。

最初に、C++ における多重継承と階層継承について説明します。

C++ のサブクラスでの多重継承

前述のように、サブクラスは、複数の継承で他の複数の基本クラスによって継承されます。 多重継承を詳細に理解するために、例を挙げてみましょう。

#include <iostream>
using namespace std;
class Base1 {
 public:
  Base1() { cout << "Base class 1" << endl; }
};

class Base2 {
 public:
  Base2() { cout << "Base class 2" << endl; }
};

class Derived : public Base2, public Base1 {
 public:
  Derived() { cout << "Derived class" << endl; }
};

int main() {
  Derived d;
  return 0;
}

出力:

Base class 2
Base class 1
Derived class

上記のコードには、Base1Base2 の 2つの基本クラスがあり、そこから Derived クラスが継承されます。 ただし、基本クラスのコンストラクターが呼び出される順序に注意する必要があります。

まず、Base2 クラス コンストラクターが呼び出されます。これは、Derived クラスが最初にそれを継承し、次に Base1 コンストラクターを継承するためです。

C++ のサブクラスでの階層的継承

前述のように、階層継承では単一の基底クラスから複数のサブクラスが継承されます。 階層的な継承を詳細に理解するために、例を挙げてみましょう。

#include <iostream>
using namespace std;
class Base {
 public:
  Base() { cout << "Base class" << endl; }
};

class Derived1 : public Base {
 public:
  Derived1() { cout << "Derived class 1" << endl; }
};

class Derived2 : public Base {
 public:
  Derived2() { cout << "Derived class 2" << endl; }
};

int main() {
  Derived1 d1;
  Derived2 d2;
  return 0;
}

出力:

Base class
Derived class 1
Base class
Derived class 2

上記のコード例には、Derived1 クラスと Derived2 クラスが共通の Base クラスから継承された 3つのクラスがあります。

C++ の継承におけるダイヤモンド問題

ひし形の問題は、階層継承と多重継承を組み合わせるときに発生します。 この問題は、クラスが互いに継承しながら菱形を形成するため、そう呼ばれます。

ABC、および D の 4つのクラスがあるシナリオがあるとします。

  1. クラス A は基本クラスとして機能します。
  2. クラス B はクラス A によって継承されます。
  3. クラス C はクラス A にも継承されます。
  4. クラス D は、クラス B とクラス C の両方に継承されます。

継承におけるダイヤモンドの問題

次に、コードを使用して発生する問題を見てみましょう。

#include <iostream>
using namespace std;
class A {
 public:
  A() { cout << "Constructor A here!" << endl; }
};

class B : public A {
 public:
  B() { cout << "Constructor B here!" << endl; }
};

class C : public A {
 public:
  C() { cout << "Constructor C here!" << endl; }
};

class D : public B, public C {
 public:
  D() { cout << "Constructor D here!" << endl; }
};

int main() {
  D d;
  return 0;
}

出力:

Constructor A here!
Constructor B here!
Constructor A here!
Constructor C here!
Constructor D here!

上記の出力は、クラス D がクラス A の 2つのコピーを取得するため、クラス A のコンストラクターが 2 回呼び出されたことを示しています。 1つはクラス B を継承し、もう 1つはクラス C を継承します。

これにより曖昧さが生じ、C++ ではダイヤモンド問題と呼ばれます。

この問題は主に、共通の基本クラスから継承された複数の基本クラスからクラスが継承される場合に発生します。

ただし、この問題は C++ で virtual キーワードを使用して解決できます。 子クラスが共通の祖父母クラスの 2つのコピーを取得しないように、2つの親クラスが仮想クラスと同じ基本クラスから継承されるようにします。

したがって、コード例では、クラス B とクラス C を仮想クラスにしています。

コード例を使用して、この問題の解決策を見てみましょう。

#include <iostream>
using namespace std;
class A {
 public:
  A() { cout << "Constructor A here!" << endl; }
};

class B : virtual public A {
 public:
  B() { cout << "Constructor B here!" << endl; }
};

class C : virtual public A {
 public:
  C() { cout << "Constructor C here!" << endl; }
};

class D : public B, public C {
 public:
  D() { cout << "Constructor D here!" << endl; }
};

int main() {
  D d;
  return 0;
}

出力:

Constructor A here!
Constructor B here!
Constructor C here!
Constructor D here!

したがって、上記の出力に示されているように、クラス A のコンストラクターのコピーを 1つだけ取得しています。 virtual キーワードは、クラス B とクラス C の両方が同じ基本クラス A から継承していることをコンパイラーに伝えます。 したがって、一度だけ呼び出す必要があります。

まとめ

この記事では、継承と継承の種類について簡単に説明しました。 ただし、主に 2つのタイプの継承について説明しました。つまり、多重継承と階層型継承で、継承のダイヤモンド問題として知られる問題が発生します。

ひし形の問題は、クラスが複数の基本クラスから継承し、それが単一の基本クラスからも継承されている場合に発生します。 ただし、この問題は C++ の virtual キーワードを使用して解決されます。

関連記事 - C++ Inheritance