非公開継承と限定公開継承
今までの継承の説明は、すべてpublic
キーワードを用いて行っていました。
この方法による継承は公開継承と言います。
public
があれば当然private
やprotected
による継承方法も存在します。
これらは非公開継承と限定公開継承といいます。
公開継承以外の継承の仕方はあまり行われるものではありません。
使いどころも難しいため、機能としては知っていても使ったことがないという人もいるようです。
非公開継承(private継承)
非公開継承は、基底クラスでpublic、protectedで宣言されているメンバを派生クラス側ではprivateにしてしまう継承です。
#include <iostream>
class BaseClass
{
private:
void func_private() { std::cout << "private" << std::endl; }
protected:
void func_protected() { std::cout << "protected" << std::endl; }
public:
void func_public() { std::cout << "public" << std::endl; }
};
//非公開継承(private継承)
class DerivedClassA : private BaseClass
{
public:
void func()
{
//func_private(); //元々エラー
func_protected();
func_public();
}
};
//DerivedClassAをさらに継承
class DerivedClassB : public DerivedClassA
{
public:
void func()
{
//func_private(); //元々エラー
//func_protected(); //DerivedClassAが非公開継承なのでエラー
//func_public(); //DerivedClassAが非公開継承なのでエラー
}
};
int main()
{
DerivedClassB dcB;
dcB.func();
//dcB.func_private(); //元々エラー
//dcB.func_protected(); //元々エラー
//dcB.func_public(); //DerivedClassAが非公開継承なためエラー
std::cin.get();
}
派生クラスのメンバ関数func
内からアクセスできる基底クラスのメンバについては、公開継承の時と変わりはありません。
privateメンバにはアクセスできず、public、protectedメンバにアクセスが可能です。
今までの継承(公開継承)では、派生クラスのインスタンスを通して基底クラスのpublicメンバにアクセスが可能でした。
しかし非公開継承にすると、基底クラスでpublic、protectedで宣言されたメンバは派生クラスではprivateとして扱われます。
そのため派生クラスのインスタンスからは基底クラスのfunc_public
関数にはアクセスができなくなっています。
(private、protectedメンバにアクセスできないのは同じです)
基底クラスを継承したクラスをさらに継承する場合も同様です。
公開継承であればpublic、protectedメンバにアクセスが可能ですが、非公開継承ではすべてのメンバにアクセスできなくなります。
公開継承との違い
公開継承以外の継承は、is-a関係ではなくなることに注意してください。
非公開継承はhas-a関係となります。
基底クラスのpublicな情報がprivateになるということは、派生クラスのインスタンスからは本来publicであった情報にアクセスできなくなるということで、is-a関係ではなくなります。
しかし、派生クラスは基底クラスの情報を持ち、扱うことができるのでhas-a関係になります。
これはつまり、公開継承以外はアップキャストができないということです。
(派生クラスのインスタンスを基底クラス型のポインタ/参照で持てない)
class BaseClass
{
};
class DerivedClass : private BaseClass
{
};
int main()
{
//エラー
BaseClass *dc = new DerivedClass();
}
合成(包含)との違い
合成は非公開継承と同じくhas-a関係です。
合成の方が基底クラスへの依存度が低く複雑さもないので、通常は非公開継承よりも合成を用いたほうが良いでしょう。
非公開継承を使うメリットは、基底クラスのprotectedメンバにアクセスしたい場合、あるいは基底クラスの仮想関数をオーバーライドしたい場合です。
これらの必要がないのであれば合成を用いるべきとも言えます。
ちなみに、あるクラスを内部に持つだけではなく実装のための手段として用いるので、非公開継承の関係をis-implemented-in-terms-of関係と言ったりします。
限定公開継承(protected継承)
限定公開継承は、基底クラスでpublicで宣言されているメンバを派生クラス側ではprotectedにしてしまう継承です。
#include <iostream>
class BaseClass
{
private:
void func_private() { std::cout << "private" << std::endl; }
protected:
void func_protected() { std::cout << "protected" << std::endl; }
public:
void func_public() { std::cout << "public" << std::endl; }
};
//限定公開継承(protected継承)
class DerivedClassA : protected BaseClass
{
public:
void func()
{
//func_private(); //元々エラー
func_protected();
func_public();
}
};
//DerivedClassAをさらに継承
class DerivedClassB : public DerivedClassA
{
public:
void func()
{
//func_private(); //元々エラー
func_protected(); //OK
func_public(); //OK
}
};
int main()
{
DerivedClassB dcB;
dcB.func();
//dcB.func_private(); //元々エラー
//dcB.func_protected(); //元々エラー
//dcB.func_public(); //DerivedClassAが限定公開継承なのでエラー
std::cin.get();
}
非公開継承とは違い、限定公開継承したクラスをさらに継承したクラスからは、基底クラスのpublic、protectedメンバにアクセスが可能となります。
しかしpublicメンバもprotectedとして継承するので、インスタンスからはメンバにはアクセスできないのは非公開継承のときと同じです。
限定公開継承は使いどころが非常に限定され、便利な使い方の例を提示することが正直できません。
無理して使うことはない機能だと思います。