C++にはJavaなどと異なりインタフェースという機能が存在しません。C++では純粋仮想関数と仮想デストラクタしかメンバをもたないクラスをインタフェースとして利用します。その際、多重継承や仮想継承の知識が必要になります。
多重継承
#include <iostream>
using namespace std;
class MyClass {
public:
virtual ~MyClass() {} // 仮想デストラクタ (インラインメンバ関数として)
virtual void Show() = 0;
virtual void SShow() = 0;
void Set(int intval) {
m_intval = intval;
}
protected:
int m_intval;
};
class MySubClassA :
public MyClass
{
public:
virtual ~MySubClassA() {}
virtual void Show() {
cout << "MySubClassA::Show(" << m_intval << ")" << endl;
}
virtual void SShow() {
cout << "MySubClassA::SShow" << endl;
}
};
class MySubClassB :
public MyClass
{
public:
virtual ~MySubClassB() {}
virtual void Show() {
cout << "MySubClassB::Show(" << m_intval << ")" << endl;
}
virtual void SShow() = 0;
};
class MySubSubClass : // 多重継承。MySubClassA, MySubClassB を介して
public MySubClassA, // MyClass を二つ継承している。MyClass の実体が二つ。
public MySubClassB
{
public:
virtual void SShow() {
cout << "MySubSubClass::Show("
<< MySubClassA::m_intval << ","
<< MySubClassB::m_intval << ")" << endl;
}
};
int main() {
MySubSubClass ss;
MySubClassA& sA = ss; // アップキャスト
MySubClassB& sB = ss;
MyClass& bA = static_cast<MySubClassA&>(ss); // アップキャスト (二段飛び)
MyClass& bB = static_cast<MySubClassB&>(ss); // (曖昧さ回避のため経由する一段目にまずキャスト)
// 実体は二つあるため別々の値を設定可能
sA.Set(1); // 「MySubClassA経由のMyClass」
sB.Set(-1); // 「MySubClassB経由のMyClass」
sA.Show(); //=> MySubClassA::Show(1)
sB.Show(); //=> MySubClassB::Show(-1)
bA.Show(); //=> MySubClassA::Show(1)
bB.Show(); //=> MySubClassB::Show(-1)
// SubSubで実装すると二つの実体の両方がオーバライドされる
ss.SShow(); //=> MySubSubClass::Show(1,-1)
sA.SShow(); //=> MySubSubClass::Show(1,-1)
sB.SShow(); //=> MySubSubClass::Show(1,-1)
bA.SShow(); //=> MySubSubClass::Show(1,-1)
bB.SShow(); //=> MySubSubClass::Show(1,-1)
return 0;
}
仮想継承
#include <iostream>
using namespace std;
class MyClass {
public:
MyClass(int intval) : m_intval(intval) {}
virtual ~MyClass() {}
virtual void Show() = 0;
protected:
int m_intval;
};
class MySubClassA :
virtual public MyClass // 仮想継承
{
public:
MySubClassA() : MyClass(1) {} // 継承における引数のあるコンストラクタ
virtual ~MySubClassA() {}
virtual void Show() {
cout << m_intval << flush;
}
};
class MySubClassB :
virtual public MyClass // 仮想継承
{
public:
MySubClassB() : MyClass(-1) {}
virtual ~MySubClassB() {}
};
class MySubSubClass :
public MySubClassA, // 多重継承
public MySubClassB // (MyClassの実体は共有され一つ。ダイヤモンド継承)
{
public:
MySubSubClass() : MyClass(0) {} // MySubClassA, MySubClassB のどちらで MyClass()
// を実行したらよいか曖昧なため MySubSubClass で実行。仮想継承のはまりどころです。
virtual void Show() { // 仮に MySubClassA, MySubClassB の両方で Show を実装したと
// すると一つの MyClass に対して Show の実体が二つになりエラーです。
// MySubSubClass で Show を実装するとエラー回避できます。
cout << "MySubSubClass::Show(" << flush;
MySubClassA::Show();
cout << "," << MySubClassB::m_intval << ")" << endl;
}
};
int main() {
MySubSubClass ss;
MySubClassA& sA = ss;
MySubClassB& sB = ss;
MyClass& b = ss; // 実体は一つであるため直接二段飛びのアップキャストが可能
// sA経由でもsB経由でも同じ実体
ss.Show(); //=> MySubSubClass::Show(0,0)
sA.Show(); //=> MySubSubClass::Show(0,0)
sB.Show(); //=> MySubSubClass::Show(0,0)
b.Show(); //=> MySubSubClass::Show(0,0)
// こうすると、MyClassのコンストラクタの引数が異なることに注目
MySubClassA _sA;
// MySubClassB _sB; // MySubClassBは抽象クラスなためエラー (Show が未実装)
_sA.Show(); cout << endl; //=> 1 (← 0でないことがポイントですね)
return 0;
}
インタフェースクラス
純粋仮想関数と仮想デストラクタしかメンバをもたないクラスをインタフェースとして利用します。インタフェースクラスを仮想継承しているクラスには所定のメンバ関数が実装されていることが保証されます。
#include <iostream>
using namespace std;
class IMyClass { // インタフェースクラス
public:
virtual ~IMyClass() {}
virtual void Show() const = 0;
};
class IMySubClassA : // インタフェースクラス
virtual public IMyClass
{
public:
virtual ~IMySubClassA() {}
virtual void ShowA() const = 0;
};
class IMySubClassB : // インタフェースクラス
virtual public IMyClass
{
public:
virtual ~IMySubClassB() {}
virtual void ShowB() const = 0;
};
class MyClass :
virtual public IMySubClassA, // 仮想継承でインタフェースクラスを利用
virtual public IMySubClassB // (実体が一つであることを保証できるため)
{
public:
virtual void Show() const {
cout << "Show" << endl;
}
virtual void ShowA() const {
cout << "ShowA" << endl;
}
virtual void ShowB() const {
cout << "ShowB" << endl;
}
};
void MyFunc(const IMyClass& obj, // アップキャスト
const IMySubClassA& objA,
const IMySubClassB& objB) {
objA.ShowA(); //=> ShowA
objB.ShowB(); //=> ShowB
// インタフェース間の継承関係のため
// ISub は IBase の関数を知っている
obj.Show(); //=> Show
objA.Show(); //=> Show
objB.Show(); //=> Show
}
int main() {
MyClass obj;
MyFunc(obj, obj, obj);
return 0;
}
0
低レイヤーのプログラミングとOS開発が趣味。C言語を使っています。
記事の執筆者にステッカーを贈る
有益な情報に対するお礼として、またはコメント欄における質問への返答に対するお礼として、 記事の読者は、執筆者に有料のステッカーを贈ることができます。
さらに詳しく →Feedbacks
ログインするとコメントを投稿できます。


