【问题标题】:Resolve C++ virtual functions from base class从基类解析 C++ 虚函数
【发布时间】:2010-02-27 16:31:37
【问题描述】:

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

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

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 :(

我做错了什么?

非常感谢

【问题讨论】:

    标签: c++ inheritance virtual-functions


    【解决方案1】:

    【讨论】:

    • +1。 @sipickles,您正在从基类的构造函数中调用虚拟方法——那是在对象的派生部分甚至被初始化之前。即使它确实调用了派生类型的方法,您希望未初始化部分的行为是什么? :)
    • 如果这本书真的是这么说的,那么这本书的地方就是垃圾桶。这本书实际上似乎暗示的是“你太愚蠢了,无法了解它是如何工作的,所以永远不要这样做”。实际上,正确的建议应该是“学习,虚拟功能在构建过程中如何工作以及如何正确使用它们”。虚函数在构建过程中工作,但有一些细节。
    • 确认,谢谢。这现在很明显了!是时候重读我的教科书了!
    • 或者,对于最简单的答案:GUIWindow 构造函数构造一个 GUIWindow。
    【解决方案2】:

    当您在构造某个类C 期间调用虚函数时(即当C 的构造函数处于活动状态时),虚机制有效,但它在受限 模式。类层次结构中虚拟调用的解析限制受当前正在构造的类 (C)。这意味着虚拟调用将解析为好像类 C 是层次结构中的“最终”类,就好像它没有后代一样。

    析构函数也是如此。

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

    您可以在 C++ FAQ 中阅读 http://www.parashift.com/c++-faq-lite/strange-inheritance.html#faq-23.5

    【讨论】:

      【解决方案3】:

      您的派生类型尚未构造。

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

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

      1. 构造动物
      2. 构造猫

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

      1. 毁灭猫
      2. 毁灭动物

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

      你将不得不:

      GUIListbox lb;
      lb.SetupCallbacks();
      

      虚拟的意义在于你可以做如下的事情:

      GuiWindow *lb = new GuiListbox();
      lb->SetupCallback();//Gets correctly resolved to GuiListBox's version
      

      【讨论】:

        【解决方案4】:

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

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

        另请参阅 more detailspossible workarounds 的 C++ FAQ 精简版。

        【讨论】:

          【解决方案5】:

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

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

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

          【讨论】:

            猜你喜欢
            • 2015-04-30
            • 1970-01-01
            • 1970-01-01
            • 2021-05-18
            • 2010-09-28
            • 2020-09-29
            • 1970-01-01
            • 2011-02-03
            • 1970-01-01
            相关资源
            最近更新 更多