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継承ですが、このように、メンバ変数に渡すインターフェイスを実装するような場合に使うことがあります