[C++] public,protected,private継承の仕様
C++は基底クラスを継承する際に以下のようにpublic等のキーワードを付与することができる。
class Deliver : public Base
今まで何も考えずにとりあえずpublicのキーワードを付与していたが、しっかり理解すべく今回は仕様を調査してみた。
派生クラスのインスタンスで使えるメソッドが変わる
以下は基底クラスをそれぞれpublic,protected,privateで継承した際の動作の違いを検証した結果だ。publicで基底を継承したクラスをインスタンス化した場合、基底クラスのpublicメソッドは呼び出して使えるが、protectedやprivateでは基底クラスのpublicメソッドを使えなくなる。
class Base
{
public:
void BasePublicFunc() {}
protected:
void BaseProtectedFunc() {}
private:
void BasePrivateFunc() {}
};
class DeliverUsePublic : public Base
{
};
class DeliverUseProtected : protected Base
{
};
class DeliverUsePrivate : private Base
{
};
int main()
{
// 基底クラスをpublic継承
DeliverUsePublic deliverUsePublic;
deliverUsePublic.BasePublicFunc(); // OK
// deliverUsePublic.BaseProtectedFunc(); // NG
// deliverUsePublic.BasePrivateFunc(); // NG
// 基底クラスをprotected継承
DeliverUseProtected deliverUseProtected;
// deliverUseProtected.BasePublicFunc(); // NG
// deliverUseProtected.BaseProtectedFunc(); // NG
// deliverUseProtected.BasePrivateFunc(); // NG
// 基底クラスをprivate継承
DeliverUsePrivate deliverUsePrivate;
// deliverUsePrivate.BasePublicFunc(); // NG
// deliverUsePrivate.BaseProtectedFunc(); // NG
// deliverUsePrivate.BasePrivateFunc(); // NG
return 0;
}
これは以下のような仕様になっているのが理由とのこと。
- public継承の場合: 基底のアクセス指定子はそのまま
- protected継承の場合: 基底のpublicのアクセス指定子はprotectedへ変わる
- private継承の場合: 基底のpublic,protectedのアクセス指定子はprivateへ変わる
ちなみに以下のようにキーワードを書かずに継承する場合、暗黙的にprivate継承になる。
class Deliver : Base
再度派生させたクラスで使えるメソッドが変わる
ここまでの内容を見るとprotected継承とprivate継承は動作の違いは無いように見えるが、動作で違いが以下のようなケースで動作に違いがでる。
以下はprotected継承したクラスとprivate継承したクラスを再度派生させたクラスを使ったコードだが、再度派生したクラスで使えるメソッドに差がでる。
protected継承した場合はBaseのprotectedメソッドも使えるが、private継承した場合は使えない。
class Base
{
public:
void BasePublicFunc() {}
protected:
void BaseProtectedFunc() {}
private:
void BasePrivateFunc() {}
};
class DeliverUseProtected : protected Base
{
};
class DeliverUsePrivate : private Base
{
};
class DeliverUseProtected2 : public DeliverUseProtected
{
public:
void func()
{
BasePublicFunc(); // OK
BaseProtectedFunc(); // OK
// BasePrivateFunc(); // NG
}
};
class DeliverUsePrivate2 : public DeliverUsePrivate
{
public:
void func()
{
// BasePublicFunc(); // NG
// BaseProtectedFunc(); // NG
// BasePrivateFunc(); // NG
}
};
public継承していないクラスは基底のポインタへキャストできない
public継承していない場合、基底クラスのpublicメソッドが使えなくなるのでis-a関係が成立しなくなる。そのため、以下のように基底クラスへのポインタへキャストができなくなる。
Base* pBase1 = &deliverUsePublic; // OK
// Base* pBase2 = &deliverUseProtected; // NG
// Base* pBase3 = &deliverUsePrivate; // NG
このように基底クラスへキャストができないとポリモーフィズムが上手く使えない。
そのため、protectedやprivate継承は個人的にはあまり使うべきではないと思う。
基底クラス継承というのはis-a関係が成立していない場合は使うべきではないが、has-a関係の場合でも継承を使って実装できるようにprotectedやprivate継承がC++で用意されている機能なのかもしれない。
Discussion