以下を検討してください。
#include <iostream>
class Base {
public:
void go(){this->doSomething();}
protected:
virtual void doSomething(){std::cout << "base";}
};
class DerivedA : public Base {
protected:
void doSomething() override {std::cout << "a";}
};
class DerivedB : public Base {
public:
Base * basePtr;
DerivedB()
:basePtr(new DerivedA())
{}
protected:
void doSomething() override {basePtr->doSomething();} // wont compile
};
int main(int , char *[])
{
DerivedB b;
b.go();
}
derivedBサブクラスのdoSomething()はコンパイルされませんが、この動作が存在する理由がわかりません!私は自分の基本クラスの仮想保護メンバー関数にアクセスしようとしていますが、許可されていると思いました。
http://en.cppreference.com/w/cpp/language/access
保護されたメンバーのアクセスに関するセクションでは、
「クラスBaseの保護されたメンバーには、1)Baseのメンバーと友人のみがアクセスできます。2)Baseから派生したクラスのメンバーと友人(C++ 17まで)がアクセスできます。ただし、ベース(これを含む)から派生したタイプ "
ポイント2によると、サブクラス(DerivedA)にキャストダウンすると、これは許可されますが、別のクラス(基本クラスではない)の保護されたメンバー関数にアクセスしているという条件と競合します。コンパイルもしません)。さらに悪いことに、派生オブジェクトのサブクラスタイプを知っている場合、その共通ベースにキャストしてもアクセスできません。
この動作が強制されるのはなぜですか?おそらく多重継承などを許すことによる副作用と思われますが、それがどういうものなのか正確には思いつきません。継承やアクセス修飾子のポイントに反する、基本クラスまたは派生クラスでのみ使用されるパブリックインターフェイスを作成する必要があるため、これは面倒です。
基本クラスの仮想保護メンバー関数にアクセスしようとしています
はい、しかしあなたは別のオブジェクトbasePtrではなくthisオブジェクトを介してこれを実行しようとしています。そして、他のオブジェクトはBaseです。どういうわけか、クラス(タイプ)とそのクラスのオブジェクト(インスタンス)を混同しています。同じオブジェクトに「入っていない」場合、Baseオブジェクトの保護されたメンバー関数を呼び出すことはできません。そして、あなたはそうではありません。派生クラス内でコードを実行しているという事実は、ここでは重要ではありません。アクセスレベルは、派生クラスのインスタンスthisではなく、アクセスしたいインスタンスbasePtrに対してチェックされていません。
そして、それはそれがそうである方法で結構です。 Baseクラスのデザイナーは、Baseクラスのオブジェクトに対してdoSomethingを呼び出さないようにしています。どこからでも!それが保護された御言葉が言うことです。ただし、doSomethingが(is-a-継承の関係の意味で)ベースであるインスタンスの実装から呼び出されることで問題ありません。派生クラスのインスタンス。