C++ 基底クラスのアクセス指定子と基底クラスを指すポインタへのキャスト
「C++ 基底クラスのアクセス指定子」では、「基底クラスのアクセス指定子は、基底クラスの public メンバと protected メンバを派生クラスでどう扱うのかということを指定しているだけだ」と言いましたが、実はそれだけではなく他にも機能があります
それは、アクセス指定子ですから当たり前ですが、派生クラスを指すポインタを基底クラスを指すポインタへキャストできる範囲も指定しているということです
例えば下記のようなクラスを継承する実装を考えてみましょう
struct ievent_client { virtual void on_event() = 0; }; class cevent_host { ievent_client* m_client; public: void raise_event(){ m_client->on_event(); } explicit cevent_host( ievent_client* client ):m_client(client){} };
private継承では、その派生クラスのメンバ関数内でのみ派生クラスを指すポインタを privateな基底クラスを指すポインタへキャストすることができます
class cfoo : private ievent_client { cevent_host m_event { this }; void on_event() override { debug_printf( "cfoo::on_event\n" ); } public: void foo(){ debug_printf( "foo{\n" ); m_event.raise_event(); debug_printf( "}\n" ); } cfoo(){} };
派生クラスの外から privateな基底クラスにアクセスすることはできませんから、派生クラスを指すポインタを privateな基底クラスを指すポインタにキャストすることはできません
int main(){ cfoo foo {}; foo.foo(); //ievent_client* p_foo = &foo; // エラー return 0; }
protected継承では、その派生クラスをさらに継承したクラスのメンバ関数内でも派生クラスを指すポインタを protectedな基底クラスを指すポインタへキャストすることができます
class cbar : protected ievent_client { protected: cbar(){} }; class cbaz : protected cbar { cevent_host m_event { this }; void on_event() override { debug_printf( "cbaz::on_event\n" ); } public: void baz(){ debug_printf( "baz{\n" ); m_event.raise_event(); debug_printf( "}\n" ); } cbaz(){} };
private継承と同様に、派生クラスの外から protectedな基底クラスにアクセスすることはできませんから、派生クラスの外で派生クラスを指すポインタを protectedな基底クラスを指すポインタにキャストすることはできません
int main(){ cbaz baz {}; baz.baz(); //ievent_client* p_baz = &baz; // エラー return 0; }
public継承では、どこでも派生クラスを指すポインタを publicな基底クラスを指すポインタにキャストすることができます
class cqux : public ievent_client { cevent_host m_event { this }; void on_event() override { debug_printf( "cqux::on_event\n" ); } public: void qux(){ debug_printf( "qux{\n" ); m_event.raise_event(); debug_printf( "}\n" ); } cqux(){} };
もちろん派生クラスの外からも publicな基底クラスには常にアクセスすることができます
これは派生クラスと publicな基底クラスとは is-a関係であることから当たり前ですね
int main(){ cqux qux {}; qux.qux(); ievent_client* p_qux = &qux; p_qux->on_event(); return 0; }
ただ、ここでおかしなことがあるのですが、気づきましたか?
cqux::on_event は private ですね
しかし、上の例ではクラスの外から呼べてしまっていますね
ievent_client::on_event は public なので当然外から呼べてしまうのですが、これは意図したものなのでしょうか
これが意図したものであるという場合には public継承で構いません
(しかし、その場合には余計な誤解を生まないように、privateではなく public に移すべきかもしれません)
これが意図したものでない。派生クラスの外から勝手に呼んでほしくないという場合には public継承ではなく private継承か protected継承を使います
あまり出番のない private継承や protected継承ですが、このように、メンバ変数に渡すインターフェイスを実装するような場合に使うことがあります