30
#include<iostream>

using namespace std;
class base
{
public:
    virtual void add() {
        cout << "hi";
    }
};

class derived : public base
{
private:
    void add() {
        cout << "bye";
    }
};

int main()
{
    base *ptr;
    ptr = new derived;
    ptr->add();
    return 0;
}

出力はbye

これがどのように実装されているかについては問題ありません。vtablesを使用していて、派生のvtableに新しいadd()関数のアドレスが含まれていることを理解しました。しかし、add()はプライベートですが、クラス外でアクセスしようとすると、コンパイラーはエラーを生成しませんか?どういうわけかそれは正しくないようです。

4

3 に答える 3

33

add()はでのみプライベートですderivedが、使用している静的タイプbase*-したがって、のアクセス制限がbase適用されます。
一般に、コンパイル時にポインタの動的タイプが何であるかを知ることさえできませんbase。たとえば、ユーザー入力に基づいて変更される可能性があります。

これはC++03§11.6によるものです:

仮想関数のアクセス規則(11節)はその宣言によって決定され、後でそれをオーバーライドする関数の規則の影響を受けません。
[...]メンバー関数が呼び出されるオブジェクトを示すために使用される式のタイプを使用して、呼び出しポイントでアクセスがチェックされます[...]。それが定義されたクラスのメンバー関数へのアクセス[...]は、一般的には不明です。

于 2010-08-31T16:11:29.933 に答える
12

public、などのアクセス修飾子は、コンパイル中privateprotectedのみ適用されます。基本クラスへのポインターを介して関数を呼び出す場合、コンパイラーは、ポインターが派生クラスのインスタンスを指していることを認識しません。コンパイラーがこの式から推測できる規則によれば、この呼び出しは有効です。

通常、派生クラスのメンバーの可視性を低下させることは意味上のエラーです。JavaやC#などの最新のプログラミング言語は、このようなコードのコンパイルを拒否します。これは、基本クラスに表示されるメンバーは、基本ポインターを介して派生クラスで常にアクセスできるためです。

于 2011-05-11T09:21:13.980 に答える
5

Georgの答えに少し追加するには:

コンパイラは派生クラスを制御できず、派生クラスについて何も保証できないことに注意してください。たとえば、自分の型をライブラリに入れて、まったく新しいプログラムで派生させることができます。ライブラリコンパイラは、派生が異なるアクセス指定子を持っている可能性があることをどのように認識しているのでしょうか。ライブラリがコンパイルされたとき、派生型は存在しませんでした。

これをサポートするために、コンパイラは実行時にアクセス指定子を認識し、プライベートメンバーにアクセスしようとした場合に例外をスローする必要があります。

于 2010-08-31T16:28:12.443 に答える