2016-09-08 25 views
3

これは他のトピックで説明されているかもしれません。しかし、私は満足のいく答えを見つけられませんでした。誰かが私のことを次のように説明できますか?私は、コードを次ていますcreatorで呼び出されたメソッドは、基底クラスから呼び出されますが、派生クラスからは呼び出されません

#include <iostream> 

class Base { 
public: 
    Base() { 
     foo(); 
    } 

    virtual void foo() { 
     std::cout << "Base::foo()" << std::endl; 
    } 
}; 

class Derived : public Base { 
public: 
    Derived(): Base() {} 

    virtual void foo() { 
     std::cout << "Derived::foo()" << std::endl; 
    } 
}; 

int main() { 
    Derived* p = new Derived(); 
} 

今私の質問ですなぜベースの作成者は、それが派生クラスでオーバーライドされるが、ベースにしていない派生クラスであるfooメソッドを呼び出しますか?

+2

'Derived'の部分はまだ構築されていません。 – Jarod42

+0

[calling-virtual-functions-inside-constructors](http://stackoverflow.com/questions/962132/calling-virtual-functions-inside-constructors)に関連する – Jarod42

答えて

1

コンストラクタ内で仮想関数を呼び出すのは悪いです。ベース

私はちょうど(

1:それは(他の言語が異なる実装を使用することができます)++、Cについては、あなたのコード内のコンストラクタを呼び出すの

注文がある。注意してください)このアプローチの欠点が表示されますされ、簡単な例を与えます

2)Derived

Baseコンストラクタの呼び出し時に、Derivedクラスで指定されたメンバは作成されません。たとえば、Derivedクラスでオーバーライドされた関数を使用できます。この場合、NOT CREATED DATAにアクセスできる関数を呼び出すことができます(派生クラスが作成されていないときに注意してください)。明らかに、それはあまりにも危険です。この時点で、作成されたデータ(この場合はBaseクラスのデータ)で動作する関数を呼び出すのは論理的です。

2

コンストラクタの内部で、構築されるオブジェクトのクラスは、コンストラクタが属するクラスのクラスです。この場合、Baseコンストラクタ内で、構築されるオブジェクトのクラスはBaseです。コンストラクタ内部から仮想呼び出しを行うことはできず、すべて静的に解決されます。

コンストラクタ内で仮想呼び出しを許可することは非常に危険です。コンストラクタの内部で、実行順序は次のとおり

  • 基底クラス(ES)コンストラクタ(S)
  • データメンバのコンストラクタは、順番にデータ・メンバーは、クラス定義
  • コンストラクタ本体
  • で宣言されています

もちろん、これは再帰的です。各基本クラスコンストラクタ内の実行順序は同じです。

Derived::foo()Derivedで定義されたデータメンバーを使用した場合の状況を想像してみてください。これらのデータメンバーは、Baseコンストラクターが実行されたときにはまだ構築されていません。その場合何が起こるでしょうか?もちろん、恐ろしい未定義の振る舞いです。

このため、コンストラクタ内で仮想関数を呼び出すことは一般的には良い考えではありません。ご覧のように、呼び出された関数は期待されたものではないかもしれません(実際には仮想呼び出しがないので)。その仮想呼び出しを行うと、未定義の動作につながる可能性があります。これは常に悪いことです。

1

フロームhere

のみローカル定義が使用されている - と何の呼び出しは、オブジェクトの派生クラス部分に触れないようにオーバーライド機能には行われません。 「ctorsで

のみローカル定義は(そのクラスの範囲の意味でローカル)が使用されます。

これは、まだ初期化されていないオブジェクトの派生クラス部分に触れることを避けるために行われます。

例は自明である。

class B { 
    public: 
     B(const string& ss) { cout << "B constructor\n"; f(ss); } 
     virtual void f(const string&) { cout << "B::f\n";} 
    }; 

class D : public B { 
    public: 
     D(const string & ss) :B(ss) { cout << "D constructor\n";} 
     void f(const string& ss) { cout << "D::f\n"; s = ss; } 
    private: 
     string s; 
    }; 
D::f

がコンストラクタB::Bに呼び出された場合、それは(コンストラクタD::Dがまだ呼び出さないため)初期化されていないstring sに値を代入しようとするだろう。そのため、C++標準の動作が定義されています。

1

C++での仮想関数呼び出しは、動的型の呼び出しで使用されるオブジェクトのタイプ(これは仮想関数と非仮想関数を区別します)に従って解決されます。クラスBaseのコンストラクタ(またはデストラクタ)は、オブジェクトの動的な型は、常にそのBaseオブジェクトはあなたのケースでDerivedようないくつかの大きな派生オブジェクト(に「埋め込まれる」場合でも、Baseことがアクティブであると見なされている

)。このため、Baseのコンストラクタがアクティブな間に行われたすべての仮想呼び出しは、Baseのメンバに解決されます。

この場合、仮想呼び出しメカニズムは正式に無効になっていないことに注意してください。 (コンストラクタからの仮想呼び出しが "静的に"解決されると主張するのは間違いです)。バーチャルコールメカニズムは引き続き通常どおり動作します。コンストラクタが現在アクティブなクラスで、階層ツリーが "上限"または "切り捨て"されているだけです。

Baseコンストラクタはアクティブですが、Derivedはまだ構築されていません。 Derivedのメンバーにアクセスすると危険です。さらに、(可能な)この制限を回避しようとすると、通常は未定義の動作につながります。同じロジックがデストラクタに対称的に適用されます。

関連する問題