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"

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

=== 更新1 ===

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# 覆盖方法一样工作?

=== 更新2 ===

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++ 可以在 Constructor 方法中运行。

虽然 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
    }
};

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

鉴于您缺乏结尾;,我猜您的背景是 Java/C#。这些语言以不同的方式实现了这个问题,但这正是 C++ 的工作方式。

于 2013-02-02T09:14:40.963 回答
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();
    })  {}
};
于 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 回答