class base {
public:
virtual void vfunc() {cout << "This is base's vfunc().\n";}
};
class derived1 : public base {
public:
void vfunc() {cout << "This is derived1's vfunc().\n";}
};
int main()
{
base *p, b;
derived1 d1;
p = &b;
p->vfunc();
p = &d1;
p->vfunc();
return 0;
}
我得到了所需的输出,然后我从基类中删除了虚拟关键字,当我运行程序时,输出是
This is base's vfunc().
This is base's vfunc().
如果有人解释在两种情况下发生的事情(就指针操纵而言),我将非常感激
由于
答案 0 :(得分:2)
在虚拟情况下,在运行时,p->vfunc()
告诉p
转到类的类定义(它的任何部分在内存中)指向者属于,并读取其vtable(或一些类似的替代实现)来确定要调用的函数。
假设vtable如下所示:
base
的vtable:
----------------
vfunc | 0x2048
----------------
derived1
的vtable:
----------------
vfunc | 0x2096
----------------
然后根据vtable指向的位置调用vfunc
的基本版本或派生版本。
在非虚拟情况下,没有发生这种情况,并且编译器在编译时将调用函数。在编译期间,所有编译器都可以确定p
是'指向基址的指针'类型,并将所有p->vfunc()
调用更改为指向地址0x2048
。
答案 1 :(得分:1)
这里发生的是有两个vtable,一个用于基础,一个用于派生。在base的vtable中你有vfunc()
的一个条目,在derived的vtable中也有一个条目,诀窍是当派生它的父级的vtable时它看起来是否覆盖它的任何父的虚方法而且它是。所以它取代了那里的东西,现在它指向它自己的版本。
换句话说,当您通过类型为base的实例调用vfunc()
时,检查基本vtable,其中包含指向base vfunc()
的指针。当您通过派生实例调用它时,您会找到指向派生的vfunc()
。
如果删除virtual关键字,编译器不再寻找虚函数,而是基于对象的运行时类型,而是基于它的静态/编译时类型。
换句话说,如果没有virtual关键字,编译器会查看base的指针类型,因此每次调用基本版本。