例として、外部に公開するインタフェースと、その実装クラスのライブラリを作るとします。
インタフェースとしてIUnknown、それを継承するISub1、ISub2というインタフェースを提供します。
内部では、それぞれ実装クラスとしてClass1、Class2を作るとします。
struct IUnknown {
virtual void hoge() = 0;
} ;
struct ISub1 : public IUnknown {
virtual void foo() = 0;
} ;
struct ISub2 : public IUnknown {
virtual void bar() = 0;
} ;
class Class1 : public ISub1 {
public:
void hoge(){ puts("call Class1::hoge()"); }
void foo(){ puts("call Class1::foo()"); }
};
class Class2 : public ISub2 {
public:
void hoge(){ puts("call Class2::hoge()"); }
void bar(){ puts("call Class2::bar()"); }
};
次に、IUnknown::hogeの実装を共通化したい
class Common : public IUnknown {
public:
void hoge(){ puts("call Common::hoge()"); }
} ;
class Class1 : public ISub1, public Common {
public:
// void hoge(){ puts("call Class1::hoge()"); }
void foo(){ puts("call Class1::foo()"); }
};
実際これでnew Class1とかすると
クラス 'Class1' は抽象クラス('IUnknown::hoge() = 0' のため)
とかのエラーが言われます。
つまりClass1にはIUnknown::hogeとCommon::hogeが別物として必要だということになり
IUnknown::hogeの実装が見つからないということです。
さてどうしよう
class Class1 : public ISub1, public Common {
public:
void hoge(){ Common::hoge(); }
void foo(){ puts("call Class1::foo()"); }
};
コンパイルは通ります。はい。ですがこれで共通化というのは。。。
template<class T> class Common : public T {
public:
void hoge(){ puts("call Common::hoge()"); }
} ;
class Class1 : public Common<ISub1> {
public:
// void hoge(){ puts("call Class1::hoge()"); }
void foo(){ puts("call Class1::foo()"); }
};
class Class2 : public Common<ISub2> {
// void hoge(){ puts("call Class2::hoge()"); }
void bar(){ puts("call Class2::bar()"); }
};
なぜこんなことが可能なのかというと、Class1が継承しているCommonテンプレートクラスはISub1インタフェースを継承していることになるからです。
ISub2* isub = new Class2;
IUnknown* iunk = new Class2;
というように普通に使えます。