很抱歉,如果这是一个骗局,我无法找到答案。
我想从基类成员调用一个函数,并让它解析为子类版本。我认为宣布虚拟会做到这一点,但事实并非如此。这是我的方法:
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 :(
我做错了什么?
非常感谢
的Si
答案 0 :(得分:8)
答案 1 :(得分:3)
在构建某个类C
期间调用虚函数时(即C
的构造函数处于活动状态时),虚拟机制正常工作,但它在< em>受限模式。类层次结构中虚拟调用的解析由当前正在构造的类(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
答案 2 :(得分:1)
您的派生类型尚未构建。
Class Cat : Animal
{
//...
};
创建Cat对象时,会发生以下情况:
如果您的对象超出范围,或者如果在堆上通过删除而被销毁,则会发生以下情况:
因此,您不应在构造函数或析构函数中调用虚函数。如果您的基类中没有实现,您甚至可以拥有pure virtual function call。
你将不得不:
GUIListbox lb;
lb.SetupCallbacks();
虚拟的目的是让你可以做以下事情:
GuiWindow *lb = new GuiListbox();
lb->SetupCallback();//Gets correctly resolved to GuiListBox's version
答案 3 :(得分:0)
问题是你试图从构造函数中调用虚函数。基类构造函数在派生类的构造函数之前调用,因此在基类构造函数运行时,尚未创建对象的“派生部分”。
对象的行为类似于基类类型的对象,直到基类构造函数完成并且派生类构造函数启动。这意味着对虚函数的调用不会调用派生类中定义的实现,它们只会从基类执行该版本。
另请参阅more details和possible workarounds的C ++ FAQ lite。
答案 4 :(得分:-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