如何从基类构造函数调用派生类虚方法?

时间:2013-02-02 09:07:10

标签: c++ c++11

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覆盖MyClass1 init方法 但它仍然显示“MyClass1 init”

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-​​&gt; init()将调用MyClass2 :: init。
但我希望C ++可以在Constructor方法中运行。

虽然可以解析obj-&gt; init()。
但我希望代码写得少一些。
有些人忘了打电话给init()。

Java / C#可以写得少做多了。但是C ++不能......这非常令人沮丧。

4 个答案:

答案 0 :(得分: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
    }
};

xxx的赋值将呈现未定义的行为,因为它的构造函数尚未运行。

鉴于你缺少结尾;,我猜你在Java / C#中的背景。这些语言以不同的方式实现了这个问题,但这就是C ++的工作原理。

答案 1 :(得分:2)

显然在构造MyClass2期间,MyClass1的构造函数将首先被调用, 派生类对象的基类部分是在派生类部件之前构造的。即使你明确地尝试创建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;
}

答案 2 :(得分:0)

我认为:
没理由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");
  }
}

答案 3 :(得分:0)

我不确定这是否合法或将来可能会中断。但是,使用lambda函数,您可以将代码发送到基类,以在构造函数期间执行。

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();
    })  {}
};