我编写了以下代码来测试我对虚拟继承的理解。显然,我仍然没有完全得到它。这是我的代码(后跟我的问题):
#include <iostream>
#include <vector>
using namespace std;
class Foo
{
public:
virtual void foo();
void foo2();
};
void Foo::foo()
{
cout << "In FOO - foo 1" << endl;
foo2();
}
void Foo::foo2()
{
cout << "In FOO - foo 2" << endl;
}
class Bar : public Foo
{
public:
void foo();
void foo2();
};
void Bar::foo()
{
cout << "In BAR - foo 1" << endl;
foo2();
}
void Bar::foo2()
{
cout << "In BAR - foo 2" << endl;
}
int main()
{
Foo* f = new Foo;
f->foo();
Foo* b = new Bar;
b->foo();
return 0;
}
这是我的理解:
指针f指向基类中的基类Foo和f->foo()
调用foo()
,后者又调用基类中的foo2()
。
指针b是基类指针,但指向派生类Bar的对象。现在,由于foo()
是一个虚函数,它调用派生类的foo()
。现在foo()
(派生类的)调用foo2()
。由于foo2()
不是虚函数,因此我希望调用基类foo2()
。但是我看到派生类的foo2()
被调用了。
所以,我期待这个输出:
In FOO - foo 1
In FOO - foo 2
In BAR - foo 1
In FOO - foo 2
但得到了这个:
In FOO - foo 1
In FOO - foo 2
In BAR - foo 1
In BAR - foo 2
为什么会这样?根据我的理解,vtable将只有foo()
而不是foo2()
的条目。那么,如何调用派生类的foo2()
?
这是我的第一篇文章。如果我违反任何发布指南,请原谅。提前谢谢!
答案 0 :(得分:8)
在Bar::foo()
,您正在呼叫foo2()
。这实际上相当于调用this->foo2()
。 this
的类型为Bar
,因此这实际上等同于:
void Bar::foo()
{
Bar *bar = this;
bar->foo2();
}
因此,此时没有涉及多态性;调用在编译时解析为Bar::foo2
,而不是在运行时动态解析。
答案 1 :(得分:1)
因为Bars foo正在调用foo2,在那个阶段它知道它是一个Bar ....
再试一次,除了直接在main中调用foo2而不是foo调用foo2
int main()
{
Foo* f = new Foo;
f->foo();
f->foo2();
Foo* b = new Bar;
b->foo();
b->foo2();
return 0;
}
答案 2 :(得分:1)
void Bar::foo()
{
cout << "In BAR - foo 1" << endl;
foo2();
}
这是因为Bar::foo2()
是由foo2()
调用的foo()
。 Bar::foo2()
位于Bar::foo()
的本地,具有优先权。这只是来自Bar::foo2()
的静态调度。
如果您期望的行为是您真正想要的,您可以通过指定其范围来选择方法,如下所示:
void Bar::foo()
{
cout << "In BAR - foo 1" << endl;
Foo::foo2();
}
所以这与动态调度无关。
答案 3 :(得分:1)
如何调用派生类的foo2()?
你希望永远不会调用Bar :: foo2。然后你的问题可以重新表述为:“为什么Bar :: foo2的目的,如果有的话?”
在处理Bar对象时调用它的目的。只要调用foo2,就会调用Bar对象的foo2。
对于foo对象,Foo :: bar2是否为虚拟才重要。如果直接处理派生类对象,继承永远不会强制您在基类中使用具有相同签名的函数。 (如果继承规则在这个问题上有所不同,那会导致太多令人不快的意外。)
你基本上做的是隐藏。通过在Bar中创建具有相同签名的函数,您已将非虚函数隐藏在基类Foo中。这通常是一个糟糕的设计,因为它不必要地复杂 - 最好为不同的东西选择不同的名称,以避免隐藏。隐藏很少是有意识的优秀设计的一部分。