1

今日、私たちのコードベースで次のような不穏な状況が見つかりました。

class Base {
public:
    virtual void Irrelevant_Function(void) = 0;

protected:
    C_Container *   Get_Container(void);
};

class A : public Base, public Not_Important {
public:
    inline C_Container *    Get_Container(void);
};

class B : public Base, protected SomethingElse {
public:
    C_Container *   Get_Container(void);
};

多くのことが Get_Container メソッドを呼び出していましたが、常に正しいメソッドを呼び出しているとは限りません。これらの関数はいずれも仮想関数ではないことに注意してください。

あいまいさを取り除くために、メソッドの名前を 、 などに変更する必要がありGet_Base_Containerます。Get_A_ContainerC++ は、呼び出す関数のバージョンを決定するためにどのような規則を使用しますか? 呼び出されるはずだった「既知の状態」から始めて、そこからバグを見つけたいと思います。

たとえば、Base へのポインターがあり、Get_Container を呼び出す場合、関数の Base バージョンを呼び出すだけだと思います。Aへのポインタがある場合はどうなりますか? Bへのポインタはどうですか?ヒープ上の A または B はどうですか?

ありがとう。

4

4 に答える 4

5

関数の呼び出し方法によって異なります。A *、 、A &またはを介し​​て呼び出す場合はA、 を呼び出しA::Get_Container()ます。Base *a 、 aを介して呼び出す場合Base &(それらが an を指している/参照している場合でもA)、 を呼び出すことになりますBase::Get_Container()

于 2011-01-27T23:13:54.757 に答える
3

仮想継承が行われていない限り、それは非常に簡単です。オブジェクトを直接操作している場合、呼び出されるのはオブジェクトのメソッドです。ポインターまたは参照を使用している場合、メソッドを決定するのはポインターまたは参照の型であり、指すオブジェクトの型は問題ではありません。

于 2011-01-27T23:18:31.253 に答える
1

メソッドは、最初にオブジェクトの静的タイプに従って検索されます。 そこで非仮想である場合は、これで完了です。これが呼び出されるメソッドです。動的タイプは、仮想メソッド、dynamic_cast、およびtypeidが使用するものであり、オブジェクトの「実際の」タイプです。静的型は、静的型システムが機能するものです。

A a;                       // Static type and dynamic type are identical.
Base &a_base = a;          // Static type is Base; dynamic type is A.

a.Get_Contaienr();         // Calls A::Get_Container.
a_base.Get_Container();    // Calls Base::Get_Container.

B *pb = new B();           // Static type and dynamic type of *pb (the pointed-to
                           // object) are identical.
Base *pb_base = pb;        // Static type is Base; dynamic type is B.

pb->Get_Container();       // Calls B::Get_Container.
pb_base->Get_Container();  // Calls Base::Get_Container.

上記では、保護されたBase :: Get_Containerメソッドにアクセスできると仮定しました。そうしないと、コンパイルエラーになります。

于 2011-01-27T23:23:29.600 に答える
1

ここで注意すべき追加の点がいくつかあります。

名前の検索は単一のスコープで行われます。たとえば、静的型 'B' のオブジェクトでメソッドを呼び出す場合、コンパイラは 'B' のインターフェイスを考慮して、有効な一致があるかどうかを判断します。存在しない場合は、Base のインターフェイスを調べて一致するものを見つけます。これが、コンパイラの観点から、あいまいさがなく、呼び出しを解決できる理由です。実際のコードにオーバーロードなどがある場合、これが問題になる可能性があります。

次に、'protected' キーワードがオブジェクト レベルではなくクラスに適用されることを忘れがちです。たとえば、次のようになります。

class Base {
protected:
    C_Container *   Get_Container(void);
};

class B : public Base{
public:
    C_Container *   Get_Container(void)
    {
        B b;
        // Call the 'protected' base class method on another object.
        return b.Base::Get_Container();
    }
};
于 2011-01-28T00:33:35.240 に答える