多重継承
CoddyのC++ジャーニー「オブジェクト指向プログラミング」セクションの一部 — レッスン 53/104。
C++では、クラスが複数の基底クラスから同時に継承することが可能です。これは多重継承と呼ばれ、異なるクラス階層の機能を組み合わせることができます。
複数のクラスから継承するには、それらをカンマで区切ります。
class Printer {
public:
void print() {
std::cout << "Printing document" << std::endl;
}
};
class Scanner {
public:
void scan() {
std::cout << "Scanning document" << std::endl;
}
};
class MultiFunctionDevice : public Printer, public Scanner {
public:
void copy() {
scan();
print();
}
};MultiFunctionDeviceクラスは、PrinterとScannerの両方のメソッドにアクセスできるようになりました:
MultiFunctionDevice mfd;
mfd.print(); // Printerから
mfd.scan(); // Scannerから
mfd.copy(); // 独自のメソッドコンストラクタは、継承宣言に基本クラスが記述されている順序(左から右)で呼び出され、デストラクタは逆の順序で呼び出されます。初期化リストで各基本クラスのコンストラクタに引数を渡すことができます:
class MultiFunctionDevice : public Printer, public Scanner {
public:
MultiFunctionDevice() : Printer(), Scanner() {}
};両方の基底クラスが同じ名前のメンバを持っている場合に、潜在的な問題が発生します。これにより曖昧さが生じるため、どちらを指しているのかを指定するためにスコープ解決演算子を使用する必要があります。
class A { public: void show() {} };
class B { public: void show() {} };
class C : public A, public B {};
C obj;
obj.A::show(); // Aのバージョンを呼び出す
obj.B::show(); // Bのバージョンを呼び出す多重継承は強力ですが、特に基底クラスが共通の祖先を共有している場合には、複雑な状況を招く可能性があります。その課題については、次のレッスンで詳しく見ていきましょう。
チャレンジ
簡単多重継承を使用して、複数の基底クラスの機能を組み合わせたスマートホームデバイスシステムを構築しましょう。WiFiに接続でき、かつ音声で制御できるデバイスを作成し、これらの機能を統合したスマートスピーカーを作成します。
コードは3つのファイルに分けて構成します:
WiFiDevice.h: ネットワークに接続可能なあらゆるデバイスを表すWiFiDeviceクラスを定義します:- protected メンバ
std::string networkName - ネットワーク名を受け取り、それを保存するコンストラクタ
Connecting to WiFi: <networkName>と出力する public メソッドconnect()WiFi Status: Connectedと出力する public メソッドstatus()
- protected メンバ
VoiceControl.h: 音声機能を備えたあらゆるデバイスを表すVoiceControlクラスを定義します:- protected メンバ
std::string assistantName - アシスタント名を受け取り、それを保存するコンストラクタ
<assistantName> is listening...と出力する public メソッドlisten()Voice Status: Activeと出力する public メソッドstatus()
- protected メンバ
main.cpp: 3つの入力(それぞれ別々の行)を読み込みます:- デバイス名 (string)
- WiFiネットワーク名 (string)
- 音声アシスタント名 (string)
WiFiDeviceとVoiceControlの両方を public 継承するSmartSpeakerクラスを定義します:- private メンバ
std::string deviceName - 3つのパラメータすべてを受け取り、初期化リストを使用してネットワーク名を
WiFiDeviceに、アシスタント名をVoiceControlに渡し、デバイス名を保存するコンストラクタ <deviceName> powering on...と出力し、その後にconnect()とlisten()を呼び出すpowerOn()メソッド- デバイス名を出力し、スコープ解決演算子を使用して両方の
status()メソッドを呼び出し、曖昧さを解決するshowAllStatus()メソッド
入力値を使用して
SmartSpeakerオブジェクトを作成します。powerOn()を呼び出し、空行を出力してから、showAllStatus()を呼び出します。
例えば、入力が Echo、HomeNetwork、Alexa の場合:
Echo powering on...
Connecting to WiFi: HomeNetwork
Alexa is listening...
Echo Status:
WiFi Status: Connected
Voice Status: ActiveshowAllStatus() メソッドは、デバイス名に続けて " Status:" を1行に出力し、その後、両方の基底クラスが同じ名前のメソッドを持っているため、曖昧さを避けるために WiFiDevice::status() と VoiceControl::status() を使用して各基底クラスの status() メソッドを呼び出す必要があります。
チートシート
C++は多重継承をサポートしており、1つのクラスが複数の基底クラスから同時に継承することを可能にします:
class DerivedClass : public BaseClass1, public BaseClass2 {
// クラス本体
};派生クラスは、すべての基底クラスのメンバにアクセスできます:
class Printer {
public:
void print() {
std::cout << "Printing document" << std::endl;
}
};
class Scanner {
public:
void scan() {
std::cout << "Scanning document" << std::endl;
}
};
class MultiFunctionDevice : public Printer, public Scanner {
public:
void copy() {
scan(); // Scannerから
print(); // Printerから
}
};基底クラスのコンストラクタは、継承リストに記述された順序(左から右)で呼び出されます。各基底クラスのコンストラクタに引数を渡すには、初期化リストを使用します:
class MultiFunctionDevice : public Printer, public Scanner {
public:
MultiFunctionDevice() : Printer(), Scanner() {}
};複数の基底クラスが同じ名前のメンバを持っている場合は、スコープ解決演算子を使用して曖昧さを解消します:
class A { public: void show() {} };
class B { public: void show() {} };
class C : public A, public B {};
C obj;
obj.A::show(); // Aのバージョンを呼び出す
obj.B::show(); // Bのバージョンを呼び出す自分で試してみよう
#include <iostream>
#include <string>
#include "WiFiDevice.h"
#include "VoiceControl.h"
using namespace std;
// TODO: WiFiDevice と VoiceControl の両方を公開継承する SmartSpeaker クラスを定義する
// - プライベートメンバ: deviceName (string)
// - コンストラクタ: deviceName, networkName, assistantName を受け取る
// 初期化リストを使用して、networkName を WiFiDevice に、assistantName を VoiceControl に渡す
// - powerOn() メソッド: "<deviceName> powering on..." と出力し、その後 connect() と listen() を呼び出す
// - showAllStatus() メソッド: "<deviceName> Status:" と出力し、その後両方の status() メソッドを呼び出す
// スコープ解決演算子 (WiFiDevice::status() および VoiceControl::status()) を使用する
class SmartSpeaker {
// TODO: ここで SmartSpeaker クラスを実装する
};
int main() {
string deviceName, networkName, assistantName;
getline(cin, deviceName);
getline(cin, networkName);
getline(cin, assistantName);
// TODO: 入力値を使用して SmartSpeaker オブジェクトを作成する
// TODO: powerOn() を呼び出す
// TODO: 空行を出力する
// TODO: showAllStatus() を呼び出す
return 0;
}
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
オブジェクト指向プログラミングのすべてのレッスン
3コンストラクタとデストラクタ
デフォルトコンストラクタ引数付きコンストラクタコピーコンストラクタムーブコンストラクタコンストラクタ初期化リスト委譲コンストラクタデストラクタ詳解Rule of Three / Five / Zeroまとめ - Stringクラス6演算子オーバーロード
演算子オーバーロード入門算術演算子のオーバーロード比較演算子のオーバーロードストリーム演算子代入演算子のオーバーロード[] と () 演算子のオーバーロード型変換演算子まとめ:Matrix クラス