7

別の質問を参照する

コードを考えてみましょう:

class Base {
public: 
    virtual void gogo(int a){
        printf(" Base :: gogo (int) \n");
    };

    virtual void gogo(int* a){
        printf(" Base :: gogo (int*) \n");
    };
};

class Derived : public Base{
public:
    virtual void gogo(int* a){
        printf(" Derived :: gogo (int*) \n");
    };
};

int main(){

    // 1)       
    Derived * obj = new Derived ; 
    obj->gogo(7);  // this is illegal because of name hiding


    // 2)      
    Base* obj = new Derived ;
    obj->gogo(7); // this is legal
}

ケース2の場合)

呼び出しobj->gogo(7)は実行時に解決されます。

obj->gogo(7)は合法なので。のvtableには、非表示になっているはずのDerivedptrが含まれ ていることを意味しているようです。virtual void gogo(int a)

私の混乱は、名前を隠すとケース1)が違法になるため、2)の呼び出しが実行時にどのように解決されるかです。

a)Derivedのvtableにはgogo(int)へのポインターが含まれていますか?

b)a)が真でない場合、仮想関数の呼び出し解決は基本クラスのvtableに進みますか。

4

4 に答える 4

5

仮想関数の呼び出しとオーバーロードの解決を混同しています。

すべての派生クラスには、基本クラスからのすべての仮想関数と追加の独自の仮想関数を含む vtable があります。これは、あなたの場合のように、実行時に呼び出しを解決するために使用されます 2)。

ケース 1) の場合、コンパイル時のオーバーロードの解決でエラーが発生します。名前が隠されているため、クラスDerivedには呼び出し可能な関数が 1 つしかありません。唯一の選択肢は、その関数を呼び出すことint*です。

于 2012-04-16T11:48:25.727 に答える
2

オーバーロードされた関数をオーバーライドしたいのですが、非表示のルールはこのようには機能しません。

「隠蔽ルールは、内側のスコープにあるエンティティが、外側のスコープにある同じ名前のものを隠すことを示しています。」

署名が異なること、つまりBase からgogo(int* a)すべての関数が隠されることは無関係であることに注意してください。gogo(whatever)オーバーライドは、関数が同じ名前、同じシグネチャ、および仮想を持つ場合にのみ発生します (したがって、のみgogo(int* a)オーバーライドされます)。

C++FAQ ブックでは、「オーバーロードされていない仮想を呼び出す非仮想オーバーロード」を使用することを提案しています。(第 29.05 章)。基本的に、基本クラスで非仮想オーバーロード関数を作成します。

gogo(int a) and gogo(int* a)

それぞれ仮想関数を呼び出します:

virtual gogo_i(int a) と virtual gogo_pi(int* a)

そして、派生クラスでこの仮想をオーバーライドします。

于 2012-04-16T13:03:54.823 に答える
1

基本的に、関数のオーバーロードは、同じ名前の関数が同じスコープで定義されている場合にのみ発生します。現在、基底クラスには独自のスコープがあり、派生クラスには独自のスコープがあります。

したがって、派生クラスで関数を再定義せずにその関数を呼び出すと、コンパイラは派生のスコープをチェックし、そこにそのような関数が定義されていないことを発見します。次に、基本クラスのスコープをチェックし、関数を発見し、それに応じて関数呼び出しをこの特定の定義にバインドします。

ただし、関数を異なるシグネチャで再定義すると、コンパイラは呼び出しをこの関数と照合し、矛盾を認識して単に文句を言います。

派生クラスの定義に「using Base::gogo;」を追加することで、この動作を変更できます。これで説明がつくことを願っています。

于 2012-04-16T12:13:25.407 に答える
0

obj2 番目を として宣言したのでBase*、vtable は のすべてのメソッドを提供しBaseます。によってオーバーライドされた仮想メソッドDerivedの場合、オーバーライドされたバージョンが呼び出されますが、他のメソッド (またはメソッドのオーバーロード) は で宣言されたもののままBaseです。

ただし、ポインタを として宣言した場合Derived*、vtable は のメソッドを与えDerived、同じ名前のメソッドを に隠しBaseます。したがって、機能しobj->gogo(7);ません。同様に、これも違法です。

Base* obj = new Derived();

// legal, since obj is a pointer to Base, it contains the gogo(int) method.
obj->gogo(7); 

// illegal, it has been cast into a pointer to Derived. gogo(int) is hidden.
(reinterpret_cast<Derived*>(obj))->gogo(7);

これは合法です:

Derived* obj = new Derived ;
obj->Base::gogo(7); // legal.

ここを参照してください。

于 2012-04-16T11:35:47.717 に答える