4

基本クラスのコンストラクター内で-仮想メソッドを呼び出すとき-派生ではなく、基本メソッドが呼び出されることを知っています-コンストラクター内での仮想関数の呼び出しを参照してください。

私の質問はこのトピックに関連しています。派生クラスのコンストラクターで仮想メソッドを呼び出すとどうなるのだろうと思っていましたが、基本部分を構築する前です。基本クラスのコンストラクター引数を評価するために仮想メソッドを呼び出すことを意味します。コードを参照してください:

class Base {
public:
  Base(const char* name) : name(name) {
    cout << "Base():" << name << endl;
  }
  virtual const char* getName() { 
    cout << "Base::getName()" << endl;
    return "Base";
  }
protected:
  const char* name;
};

class Derived : public Base {
public:
  Derived() : Base(getName()) {
    cout << "Derived():" << name << endl;
  }
  virtual const char* getName() { 
    cout << "Derived::getName()" << endl;
    return "Derived";
  }
};

int main() {
  Derived d;
}

コンパイラ g++ (4.3.x-4.5x バージョン) の出力は次のとおりです。

Derived::getName()
Base():Derived
Derived():Derived 

しかし、私は期待します:

Base::getName()
Base():Base
Derived():Base

これは間違っているようには見えませんが、次の例を考えてみましょうsegmentation fault

class Derived : public Base {
public:
  Derived() : Base(getName()), name(new string("Derived")) {
    cout << "Derived():" << Base::name << endl;
  }
  virtual const char* getName() { 
    cout << "Derived::getName()" << endl;
    return name->c_str();
  }
private:
  string* name;
};

答えてください: これは正しい g++ の動作ですか? それについてどのC++標準が述べていますか? 多分それは未定義の動作ですか?

[UPDATE1] Robert と Oli の回答を考慮して、最初の例を変更しました。次に、 getName() が「仮想」と呼ばれ、セグメンテーション違反が発生します。この部分についても私の質問に答えてください。

const char* virtualGetName(Base* basePtr)
{
  return basePtr->getName();
}

class Derived : public Base {
public:
  Derived() : Base(virtualGetName(this)) {
    cout << "Derived():" << Base::name << endl;
  }
  virtual const char* getName() { 
    cout << "Derived::getName()" << endl;
    return "Derived";
  }
};
4

2 に答える 2

11

あなたの例はすべて未定義の動作を示します。C++ 言語標準は次のように述べています (C++11 §12.6.2/13)。

メンバー関数 (仮想メンバー関数を含む) は、構築中のオブジェクトに対して呼び出すことができます。同様に、構築中のオブジェクトは、typeid演算子または aのオペランドにすることができますdynamic_cast

ただし、基本クラスのすべてのmem-initializersが完了する前に、これらの操作がctor-initializer (またはctor -initializerから直接または間接的に呼び出される関数) で実行された場合、操作の結果は未定義です。

クラス コンストラクターgetName()の初期化リスト ( ctor-initializer )からメンバー関数を呼び出しています。Derivedこのメンバ関数呼び出しは、イニシャライザ自体への引数であるため、イニシャライザ ( の mem-initializer)が完了Baseする前に実行する必要があります。Base

したがって、動作は未定義です。

原則として、構築中または破棄中に仮想関数を呼び出さないでください

于 2012-07-04T21:38:24.170 に答える
1

派生クラスのコンストラクターで仮想メソッドを呼び出すとどうなるのだろうと思っていましたが、基本部分を構築する前です。

これをやっているように見えるかもしれませんが、そうではありません。

最初の例でDerived::getName()は、に依存しないthisため、メソッド呼び出しは機能します。2番目の例でDerived::getName()は、に依存しthisます。this->nameはまだ設定されていないため、未定義の場所を指し、segfault が発生します 。

this->nameDerivedコンストラクターが最初に行うことはコンストラクターの呼び出しであるため、まだ設定されていませんBaseBaseコンストラクターに渡すパラメーターを指定すると、最初にそれらが処理されます。次に、コンストラクターを呼び出して、クラスのメンバー変数をインスタンス化します。初期化子リストを使用してこれらのコンストラクターにパラメーターを渡すことができますが、呼び出される順序を変更することはできません。このステップで、namegets が初期化されます。最後に、Derivedコンストラクターの本体が実行されます。

于 2012-07-04T21:26:29.610 に答える