我试图将它映射到我脑海中,但说实话,我不知道这里到底发生了什么。
当我在下面的示例中添加和删除虚拟关键字时,究竟发生了什么?
#include <iostream>
#include <string>
class A {
public:
A() { me = "From A"; }
void caller() { func(); }
virtual void func() { std::cout << me << std::endl; } // THIS LINE!
private:
std::string me;
};
class B : public A {
public:
B() { me = "From B"; }
void func() { std::cout << me << std::endl; }
private:
std::string me;
};
int main() {
A a;
a.caller();
B b;
b.caller();
return 0;
}
使用虚拟关键字打印&#34;从A&#34;,然后&#34;从B&#34;。
如果没有虚拟关键字,则会打印&#34;从A&#34;,然后&#34;从A&#34;。
到目前为止,这是我唯一一次在没有指针的情况下使用虚拟功能。我认为如果删除了虚拟关键字,编译器会做标准的事情,即重载继承的函数并最终打印&#34;从A&#34;和&#34;从B&#34;反正。
我认为这比VTable更深入,而且更多的是它在特定情况下的行为方式。 B甚至有VTable吗?
答案 0 :(得分:1)
电话
func()
相当于
this->func()
所以是涉及的指针。
但是,没有必要让指针来理解行为。
即使直接打电话,例如当b.func()
在静态已知类型中是虚拟的时,func
必须使用,就像它是虚拟呼叫一样。编译器可以基于知道b
的最派生类型来优化它。但这是一种不同的考虑因素(优化可以做任何事情)。
答案 1 :(得分:0)
在您的示例中,您不会看到差异:
使用虚函数,编译器将通过VTable生成调用,并且在运行时,每个对象将为其真实类调用正确的函数。
使用非虚函数,编译器在编译时根据对象定义的类确定要调用的正确函数。
现在尝试以下操作,以查看正在运行的虚拟功能:
A *pa = &b; // pointer to an A: valid as b is a B wich is also an A.
pa -> caller(); // guess what will be called if virtual or not.
无需指向试验虚拟功能的指针。你也可以用引用观察到相同的效果:
A& ra = b; // create a reference to an A, but could as well be a parameter passed by reference.
ra.caller();
虚函数对于多态非常有用。这个想法是你使用类的一般对象,但是你在编译时不知道,如果在运行时对象真的属于这个类,或者它不是一个更特殊的对象(继承)来自班级)。
答案 2 :(得分:0)
除了虚拟发送的问题,可能带来额外混淆的是,你有两个me
,一个在A
中声明,另一个在B
中声明。这是两个截然不同的对象。
B
类型的对象有两个类型为std::string
的数据成员;一个独立,一个纳入类型A
的子对象。但是,后者不能立即在B
类型的方法中使用,因为它的名称被此类中引入的新me
所黯然失色(尽管您可以使用限定名称A::me
引用它)。
因此,即使A::func
和B::func
的实体看起来相同,但两者中使用的标识符me
都指向不同的成员。