2
class MyClass1
{
public:
  MyClass1()
  {
    init();
  }
  virtual void init()
  {
    printf("MyClass1 init");
  }
}

class MyClass2 : public MyClass1
{
public:
  virtual void init()
  {
    printf("MyClass2 init");
  }
}

int main()
{
  MyClass2 *obj = new MyClass2();
  return 0;
}

これを実現したい

"MyClass2 init"

しかし、それは実際にメッセージを表示します

"MyClass1 init"

基本クラスコンストラクターから派生クラス仮想メソッドを呼び出す方法は?

=== update1 ===

class MyClass1
{
public:
  MyClass1()
  {
    init();
  }
  virtual void init()
  {
    printf("MyClass1 init");
  }
}

class MyClass2 : public MyClass1
{
public:
  MyClass2()
    : MyClass1()
  {
  }

  virtual void init()
  {
    printf("MyClass2 init");
  }
}

MyClass2がMyClass1initメソッドをオーバーライドすることを望みますが、それでも「MyClass1init」が表示されます

C++はjava/C#オーバーライドメソッドのようにどのように機能しますか?

=== update2 ===

class MyClass1
{
public:
  MyClass1()
  {
    init();   <--- can't work like test method ??? 
  }
  void test()
  {
    init();   <--- work fine
  }
  virtual void init()
  {
    printf("MyClass1 init");
  }
}

class MyClass2 : public MyClass1
{
public:
  MyClass2()
    : MyClass1()
  {
  }

  virtual void init()
  {
    printf("MyClass2 init");
  }
}

obj-> init()がMyClass2::initを呼び出すことはわかっています。
しかし、C++がコンストラクターメソッドで実行できることを願っています。

obj-> init()は解決できますが。
しかし、私はコードが少し少なく書くことができることを願っています。
init()を呼び出すのを忘れる人もいます。

Java / C#は、より少ない書き込みでより多くのことを実行できます。しかし、C++はできません....これは非常に苛立たしいことです。

4

4 に答える 4

6

明らかに、の構築中に、MyClass2MyClass1コンストラクタが最初に呼び出されます。つまり、派生クラスオブジェクトの基本クラス部分は、派生クラスパーツが構築される前に構築されます。のオブジェクトを明確に作成しようとしても、MyClass2基本クラスの構築中に、仮想関数が派生クラスに分類されることはありません。

基本クラスコンストラクターは派生クラスコンストラクターの前に実行されるため、基本クラスコンストラクターの実行時に派生クラスデータメンバーは初期化されていません。基本クラスの構築中に呼び出された仮想関数が派生クラスにダウンした場合、派生クラス関数はほぼ確実にローカルデータメンバーを参照しますが、それらのデータメンバーはまだ初期化されていないため、未定義の動作が発生します。

あなたのクラスを参照してください、なぜあなたはvirtual initそもそも持っているのですか?コンストラクターはそれぞれ作業を行う必要があります。

基本クラスコンストラクターから派生クラス仮想メソッドを呼び出す方法は?

それは悪い考えです、決してそれをするべきではありません!

class MyClass1
{
public:
  virtual ~MyClass1() { }
  virtual void doSomething()
  {
    printf("MyClass1 init");
  }
};

class MyClass2 : public MyClass1
{
public:
  virtual void doSomething()
  {
    printf("MyClass2 init");
  }
};

int main()
{
  MyClass1*obj = new MyClass2();
  obj->doSomething();
  delete obj;
  return 0;
}
于 2013-02-02T09:34:53.110 に答える
2

一般的に言えば、できません。これは、MyClass2コンストラクターがまだ実行されていないためです。したがって、MyClass2::init()呼び出された場合、メンバー変数は初期化されません。このコンストラクターが開始されるまで、仮想目的のオブジェクトはMyClass1インスタンスと見なされます。

例えば:

class MyClass2 : public MyClass1
{
private:
    std::string xxx;
public:
    virtual void init()
    {
        xxx = "ops!"; //undefined if called from base class constructor
    }
};

toの割り当てxxxは、コンストラクターがまだ実行されていないため、未定義の動作をレンダリングします。

あなたの終わりの欠如を考えると、;私はあなたのJava / C#のバックグラウンドを推測しています。これらの言語はこの問題を異なる方法で実装していますが、それがC++の動作方法です。

于 2013-02-02T09:14:40.963 に答える
0

これが合法であるか、将来的に破綻する可能性があるかどうかはわかりません。ただし、ラムダ関数を使用すると、コンストラクター中に実行するコードを基本クラスに送信できます。

class Base {
public:
    Base(std::function<void()> fnCall=nullptr) {
        if (fnCall) fnCall();
    }
    Base(const Base &base, std::function<void()> fnCall = nullptr) {
        DoSomethingAtBaseHere();
        if (fnCall) fnCall();
    }
};

class Derived: public Base {
public:
    Derived(std::function<void()> fnCall=nullptr) : Base([&]() {
        DoSomethingAtDerivedHere();
        if (fnCall) fnCall();
    }) {}
    Derived(const Derived &oth, std::function<void()> fnCall=nullptr) : 
    Base(oth, [&]() {
        DoSomethingInCopyConstructorAtDerivedHere();
        if (fnCall) fnCall();
    })  {}
};
于 2020-06-23T17:14:42.743 に答える
-1

私は思う:
C++がjava/ C#仮想コンストラクターメカニズムを実行できない理由はありません。
C++のルールを破りたい。
また、C++がより少ない書き込みでより多くのことを実行できることを願っています。
C ++が改善されなければ進歩しないので、より多くの人が簡単に入ることができません。

class MyClass1
{
public:
  MyClass1()
  {
    init();
  }

  typedef void (MyClass1::virtual_init)();
  MyClass1(virtual_init vinit)
  {
    (this->*vinit)();
  }

  //virtual void init()
  void init()
  {
    printf("MyClass1 init");
  }
}

class MyClass2 : public MyClass1
{
public:
  MyClass2()
    : MyClass1(&MyClass2::init)
  {
  }

  //virtual void init()
  void init()
  {
    printf("MyClass2 init");
  }
}
于 2013-02-03T02:06:59.010 に答える