web-dev-qa-db-ja.com

基本クラスからの仮想関数呼び出し

私たちが持っていると言います:

_
Class Base
{   
    virtual void f(){g();};
    virtual void g(){//Do some Base related code;}
};

Class Derived : public Base
{   
    virtual void f(){Base::f();};
    virtual void g(){//Do some Derived related code};
};

int main()
{
    Base *pBase = new Derived;
    pBase->f();
    return 0;  
}

_

g()から呼び出されるBase::f()はどれですか? Base::g()またはDerived::g()

ありがとう...

49
Gal Goldman

派生クラスのgが呼び出されます。ベースで関数を呼び出したい場合は、呼び出します

Base::g();

代わりに。派生バージョンを呼び出したいが、基本バージョンを呼び出したい場合は、gの派生バージョンが最初のステートメントで基本バージョンを呼び出すように調整します。

virtual void g() {
    Base::g();
    // some work related to derived
}

ベースの関数が仮想メソッドを呼び出し、制御が派生クラスに転送されるという事実は、テンプレートメソッドのデザインパターンで使用されます。 C++の場合、 Non-Virtual-Interface として知られています。 C++標準ライブラリでも広く使用されています(たとえば、C++ストリームバッファには関数pub...実際の作業を行う仮想関数を呼び出します。たとえば、pubseekoffは保護されたseekoffを呼び出します。この答えでその例を書きました: どのようにしてオブジェクトの内部状態を検証しますか?

Baseのコンストラクターでgを呼び出さない限り、Derived :: gです。 BaseコンストラクターはDerivedオブジェクトが構築される前に呼び出されるため、Derived :: gを論理的に呼び出すことはできません。まだ構築されていない変数を操作する可能性があるため、Base :: gが呼び出されます。

8
Syed Lavasani

pBaseは、ベースへのポインターです。 pBase = new Derivedは、Derived-Derived is-a Baseへのポインターを返します。

したがって、pBase = new Derivedは有効です。

pBaseはBaseを参照するため、DerivedはBaseであるかのように見えます。

pBase-> f()は、Derive :: f()を呼び出します。

次に、コードで次のことを確認します。

Derive :: f()-> Base :: f()-> g()-but which g ??

それは、Derive :: g()を呼び出します。これは、pBaseが「指す」gであるためです。

回答:Derive :: g()

5
Jacob

まあ...これがコンパイルされるかどうかわかりません。以下、

Base *pBase = new Derived;

次のものがない限り無効です:

Class Derived : public Base

それはあなたが意味したいですか?これがあなたが意味したいなら、

pBase->f();

次に、呼び出しスタックは次のようになります。

Derived::f()
    Base::f()
        Derived::g()
2
user48956

実際にコードを実行すると、Derived :: g()が呼び出されていることがわかります。

1
KJAWolf

g()=仮想と定義したので、最も派生したg()はクラスのvtableで検索され、現在アクセスしているコードを入力します。

C++ FAQ仮想関数について を参照してください。

1
Thorsten79

派生クラスのメソッドが呼び出されます。

これは、仮想関数を持つクラスおよびそれらの関数をオーバーライドするクラス内にvtableを含めるためです。 (これは動的ディスパッチとも呼ばれます。)実際に何が起こっているのか:クラスごとにvtableが1つしかないため、Baseに対してvtableが作成され、Derivedに対してvtableが作成されます。 pBaseは仮想でオーバーライドされている関数を呼び出しているため、Derivedのvtableへのポインターが呼び出されます。 _d_ptr_(vpointerとも呼ばれます)と呼びます:

_int main()
{
    Base *pBase = new Derived;
    pBase->d_ptr->f();
    return 0;  
}
_

これで、d_ptrはDerived::f()を呼び出します。これはBase::f()を呼び出し、vtableを見て、使用するg()を確認します。 vpointerはDerivedg()のみを知っているため、これが使用されます。したがって、Derived::g()が呼び出されます。

0
Jossie Calderon

テンプレートメソッドパターン を発明しようとしていると思います

0
Evgeny Lazin