17
# include <iostream>
using namespace std;

class A
{
    public:
    virtual void f()
    {
        cout << "A::f()" << endl;
    }
};
class B:public A
{
    private:
    virtual void f()
    {
        cout << "B::f()" << endl;
    }
};
int main()
{
    A *ptr = new B;
    ptr->f();
    return 0;
}

このコードは正しく機能し、B::f() を出力します。それがどのように機能するかは知っていますが、なぜこのコードが許可されているのですか?

4

6 に答える 6

14

アクセス制御は、実行時ではなくコンパイル時に実行されます。一般に、 がf()指すオブジェクトの実行時型を の呼び出しで知る方法はptrないため、派生クラスのアクセス指定子はチェックされません。そのため、呼び出しが許可されます。

クラス B がプライベート関数を使用してオーバーライドできる理由については、よくわかりません。確かに、B は A からの継承によって暗示されたインターフェイスに違反していますが、一般に、C++ 言語は常にインターフェイスの継承を強制しているわけではないため、それが単なる間違いであるという事実は、C++ があなたを止めるという意味ではありません。

したがって、このクラス B にはおそらくいくつかのユースケースがあると思います-置換は動的ポリモーフィズムで引き続き機能しますが、静的に B は A の置換ではありません (たとえば、 を呼び出すテンプレートがありf、それは A を引数として動作しますが、動作しません) B を引数として)。それがまさにあなたが望む状況があるかもしれません。もちろん、それは他の考慮事項の意図しない結果である可能性があります。

于 2011-02-14T10:56:57.183 に答える
2

f は A のインターフェイスでパブリックであるため、このコードは許可されます。派生クラスは、基本クラスのインターフェイスを変更できません。(仮想メソッドをオーバーライドしても、インターフェイスが変更されることはなく、ベースのメンバーが非表示になることもありませんが、どちらもそのように見える場合があります。) 派生クラスがベースのインターフェイスを変更できる場合、それは"is a" 関係に違反します。

A の設計者が f にアクセスできないようにしたい場合は、保護または非公開としてマークする必要があります。

于 2011-02-14T11:01:58.693 に答える
0

関数アクセス制御チェックは、c++関数呼び出しの後の段階で行われます。高レベルの順序は、名前の検索、テンプレート引数の推定(存在する場合)、オーバーロードの解決、アクセス制御(パブリック/プロテクト/プライベート)のチェックのようになります。

しかし、スニペットでは、基本クラスへのポインターを使用しており、基本クラスの関数f()は実際にパブリックであり、コンパイラーがコンパイラー時に認識できる範囲であるため、コンパイラーは確実にスニペットを通過させます。

A *ptr = new B;
ptr->f();

ただし、上記はすべてコンパイル時に発生するため、実際には静的です。vtableとvpointerを利用することが多い仮想関数呼び出しは実行時に発生する動的なものであるため、仮想関数呼び出しはアクセス制御と直交しています(仮想関数呼び出しはアクセス制御の後に発生します)。そのため、f()の呼び出しは実際に終了しました。 f()は、アクセス制御がプライベートであるかどうかに関係なく。

しかし、あなたが使用しようとすると

B* ptr = new B;
ptr->f()

これは、vpointerとvtableにもかかわらず通過せず、コンパイラーはコンパイル時にコンパイルすることを許可しません。

しかし、試してみると:

B* ptr = new B;
((static_cast<A*>(ptr))->f();

これは問題なく機能します。

于 2012-10-10T17:06:40.613 に答える
0

基本クラスは、継承されたすべての子のインターフェイスを定義しています。上記のアクセスを禁止する必要がある理由がわかりません。「B」から派生したクラスを試して、Base インターフェイスを使用して を呼び出すと、エラーが発生します。

乾杯!

于 2011-02-14T10:56:20.013 に答える
0

スティーブの答えに加えて:

  • B は公に A から導出されます。これは Liskov の代入可能性を意味します。
  • f をプライベートにオーバーライドすることはその原則に違反しているように見えますが、実際には必ずしもそうとは限りません。コードが邪魔になることなく、B を A として引き続き使用できます。
  • B は A の Liskov 置換可能である必要がありますが、B は実際には (Liskov 置換可能な方法で) A に関連していない別の階層のルートでもあり、f はもはやパブリック インターフェイスの一部ではありません。 . 言い換えれば、B から派生したクラス C は、B へのポインターを介して使用され、f を非表示にします。
  • しかし、これは実際には非常にありそうもないことであり、A から B をプライベートに、または保護された方法で派生させる方がおそらく良い考えだったでしょう。
于 2011-02-14T12:31:25.383 に答える
-1

Java とほぼ同じように、C++ ではメソッドの可視性を高めることはできますが、減らすことはできません。

于 2011-02-14T11:04:16.460 に答える