4

对不起,如果这是一个骗局,我找不到完全正确的答案。

我想从基类成员调用一个函数,并将其解析为子类版本。我以为宣布它是虚拟的就可以了,但事实并非如此。这是我的方法:

class GUIWindow
{
public:
    GUIWindow()
    {
        SetupCallbacks();
    }

    virtual void SetupCallbacks()
    {
         // This function always called
    }
};

class GUIListbox : public GUIWindow
{
public:
    void SetupCallbacks()
    {
        // This never called
    }
};

GUIListbox lb; // GUIWindow::SetupCallbacks is called :(

我究竟做错了什么?

非常感谢

4

5 回答 5

8

永远不要在构造函数中调用虚函数。

于 2010-02-27T16:36:37.253 回答
3

当你在某些类的构造过程中调用虚函数时C(即当 的构造函数C处于活动状态时),虚机制工作,但它在受限模式下工作。类层次结构中虚拟调用的分辨率受当前正在构造的类的限制(C。这意味着虚拟调用将解析为好像该类C是层次结构中的“最终”类,就好像它没有后代一样。

析构函数也是如此。

在您的示例中,您正在从 class 的构造函数调用虚函数GUIWindow。只要 的构造函数GUIWindow处于活动状态,它的虚拟机制就会像没有其他从GUIWindow. 这种机制将完全忽略GUIListbox类的存在。这就是为什么虚拟呼叫的解析“停止”在GUIWindow和呼叫GUIWindow::SetupCallbacks,就好像GUIListbox::SetupCallbacks不存在一样。

您可以在 C++ 常见问题解答 http://www.parashift.com/c++-faq-lite/strange-inheritance.html#faq-23.5

于 2010-02-27T17:08:18.020 回答
1

您的派生类型尚未构造。

Class Cat : Animal
{
//...
};

当您创建一个创建 Cat 对象时,会发生以下情况:

  1. 建造动物
  2. 构造猫

当您的对象超出范围或在堆上通过 delete 销毁时,会发生以下情况:

  1. 破坏猫
  2. 毁灭动物

因此,您不应在构造函数或析构函数中调用虚函数。如果您的基类中没有实现,您甚至会有一个纯虚函数调用。

你将不得不:

GUIListbox lb;
lb.SetupCallbacks();

虚拟的意义在于,您可以执行以下操作:

GuiWindow *lb = new GuiListbox();
lb->SetupCallback();//Gets correctly resolved to GuiListBox's version
于 2010-02-27T16:40:12.690 回答
0

问题是您试图从构造函数中调用虚函数。基类构造函数在派生类的构造函数之前调用,因此当基类构造函数运行时,对象的“派生部分”尚未创建。

该对象的行为类似于基类类型的对象,直到基类构造函数完成并且派生类构造函数启动。这意味着对虚函数的调用不会调用派生类中定义的实现,它们只会执行基类中的版本。

另请参阅 C++ FAQ lite 了解更多详细信息可能的解决方法

于 2010-02-27T16:41:54.730 回答
-1

快速的答案是您可能必须声明一个构造函数,GUIListbox其中调用SetupCallbacks. 原因更复杂:因为 GUIListbox 没有声明构造函数,所以当您声明该类型的对象时,GUIWindow构造函数会被调用。当GUIWindow构造函数运行时,对象还不是 GUIListbox。从编译器的角度来看,只有在该类的(空)构造函数启动后,它才会变成一个 GUIListbox。因此,当第一个构造函数运行时,会调用 GUIWindow 中的方法。IOWs,这不起作用,并且永远不会按照您想要的方式在 C++ 中工作。

这是详细描述此陷阱的参考:http ://www.artima.com/cppsource/nevercall.html 。

这是我最喜欢的 C++ 资源的解释:http: //yosefk.com/c++fqa/inheritance-mother.html#fqa-23.5

于 2010-02-27T16:39:46.473 回答