多重継承
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つの入力を読み込みます(それぞれ別の行)。- デバイス名(文字列)
- WiFiネットワーク名(文字列)
- 音声アシスタント名(文字列)
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()メソッドは、1行にデバイス名と" Status:"を続けて出力し、その後、両方の基底クラスに同じ名前のメソッドがあるため曖昧さを避ける目的で、WiFiDevice::status()とVoiceControl::status()を使って各基底クラスのstatus()メソッドを呼び出します。
自分で試してみよう
#include <iostream>
#include <string>
#include "WiFiDevice.h"
#include "VoiceControl.h"
using namespace std;
// TODO: WiFiDevice と VoiceControl の両方を public 継承する 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クラス自分で練習してみよう: C++オンラインコンパイラ