継承

2016-12-10 17 views
3

は、私は、次のクイズの問題があります。継承

#include <iostream> 
using namespace std; 

struct A 
{ 
    void ohai() { cout << "ohai" << endl; } 
}; 

struct C : private A { 
    friend int main(); 
}; 

struct X : C {}; 
struct Y : private C {}; 

int main() {  
    C().ohai() // OK 
    X().ohai(); // OK 
    Y().ohai(); // Not OK 
} 

を事は、クラスCは、個人から継承することです。したがって、Aのすべてのメンバーは、今でもpublicであっても、Cでプライベートになります。 クラスCはmain関数で親しみやすさを宣言します(これはOKで、今は 'main'関数がCのprivateメソッドを呼び出せるようになりました)。 次に、クラスXとクラスYがあります。どちらもCから派生しています。 Xは公にCから派生し、YはCからプライベートに派生します。

私はクラスCのインスタンスを作成し、Aからプライベートに継承された 'ohai'メソッドを呼び出します。mainはCのフレンドですので、これはうまくいきます。 次にクラスXのインスタンスを作成して呼び出します。 'ohai' - これは驚くほど効果的です! しかし、クラスYのインスタンスを作成すると、これはうまくいきません!

私は継承が不可侵ではないことを知っています。これは、クラスXのオブジェクトに対して 'メイン'が 'オハイ'メソッドを呼び出すことを可能にする親和性の継承ではありません。 クラスYのものは、継承のタイプをプライベートに変更してこれを停止するだけで十分であることを確認しますワーキング。

なぜ私はXオブジェクト上で 'ohai'を呼び出すことが大丈夫ですか?継承は関数のレベルを(プライベートからパブリックに)バンプすることはできません。したがって、XがCから継承したとしても、CからのすべてのプライベートメソッドはXでプライベートにしておかなければなりません。親和性は継承できないので、 クラスYはCから個人的に継承しています。これにより、CのすべてのメンバーはXでは非公開になりますが、 'ohai'メソッドはすでにCでプライベートになっています(CはAからプライベートに継承されています)。 anythigを変更すべきではありませんが、どういうわけか(Xと比較して)します。

この問題を理解してもらえますか? よろしくお願いします。 YotKay

答えて

1

ここで私の答えは完全にはわかりません。私が間違っていれば私を修正してください。私はここに何が起こるかと考えている何

あなたがX().ohai()を呼び出すときに、Xthisポインタが暗黙のうちにmainに友情を付与されたその基底クラスC、にキャストされていることです。より正確に言えば、コンパイラがにあるohaiを検索すると、Xで見つからず、直接ベースのクラスCではなく、Aにあります。フレンドシップと他のアクセス制御機構は、コンパイラがメンバ関数を見つけるべき場所を妨げない。その効果は、メンバーが見つかった後に発生します。コンパイラがAohaiを見つけたら、ポインタ(this)をA *にキャストする必要があります。 (これまでは、私は正しいと確信しています)。 XCから継承されているため、C *にキャストできます。そしてCmainの友情を与えるので、それをA *にキャストし続けることができます。したがって、ohaiに電話することができます。 (最後の部分はあまり正確ではありませんが、理解しやすくなります)。第二キャストがあなたのクランに次のエラーを与えるだろうしながら、最初のキャストが、何の問題もありません

int main() 
{ 
    X x; 
    Y y; 
    static_cast<A *>(&x); 
    static_cast<A *>(&y); 
} 

たとえば、次のようにしてみてください、

test.cpp:24:22: error: cannot cast 'Y' to its private base class 'A' 
    static_cast<A *>(&y); 
        ^
test.cpp:15:11: note: constrained by private inheritance here 
class Y : private C 
      ^~~~~~~~~ 

削除した場合friend宣言では、最初のキャストも失敗しますが、別の理由で失敗します。

正しいですが、あなたは友人の宣言を削除し、次のことをしようとした場合、それは実際にここに

何が起こったのかとは何の関係もありません、

int main() 
{ 
    X x; 
    Y y; 
    static_cast<A *>(&x); 
    static_cast<C *>(&x); 
} 

最初の「友情を継承することはできません」キャストはアクセス制御のために失敗し、2番目は成功します。

メインへの友情は、Cのプライベートメンバーだけでなく、プライベートベースクラスへのアクセスを許可するだけでなく、この問題については、friend宣言

int main() 
{ 
    C c; 
    static_cast<A *>(&c); 
} 

最終でとせずに次のことを試して、試してください次

struct A { 
    void ohai() { std::cout << "ohai" << std::endl; } 
}; 

struct C : private A { 
    using A::ohai; 
}; 

struct X : C { 
    void foo() { C::ohai(); } // static_cast<C *>(this)->ohai(); 
    void bar() { A::ohai(); } // static_cast<A *>(this)->ohai(); 
}; 

int main() 
{ 
    X x; 
    x.foo(); 
    x.bar(); 
    x.ohai(); 
} 

機能fooはOKになりますが、barは失敗します。 Xは、その直接基盤にキャストすることができるので、C で公表されたohaiを呼び出します。しかしAには対応できず、ohaiA

+0

にお電話いただきありがとうございます。多分あなたは正しいです。私はあなたの例を試してみました.GCCの例(1)の場合、私は両方のケースで同じエラーが発生しました(エラー: 'A'はアクセス不可能な 'X'のベースです)、友好宣言の有無にかかわらず(Clang、あなたが書いたことによると)。 2番目の例では、あなたが書いたとおり、最初のキャストは上記と同じエラーで失敗し、2番目のキャストは正常に動作します。 – YotKay

+0

@YotKayちょうど "多分"私の答えについてあまり確信してはいけません。何が起こったのか説明できると思う。しかし、私が言ったところでは、標準で特定のフレーズを見つけられませんでした。*この時点まで、私は正しいと確信しています*。与えられた時間より多くの情報を得た他の人があなたに良い答えを与えるかもしれません –

+0

@YotKayアクセスコントロールはメンバー関数の呼び出しを解決した後に来て、メンバ関数呼び出しは暗黙的にベースクラスにキャストされます。 –