2

如果这个问题对你来说很愚蠢,请原谅我。我是 C++ 的新手,我正在研究朗姆酒时间多态性。我想知道当基类对象调用派生类函数(非虚拟)时会发生什么。例如看代码

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;
}

我得到了想要的输出,然后我从基类中删除了 virtual 关键字,当我运行程序时,输出是

This is base's vfunc().
This is base's vfunc().

如果有人解释在这两种情况下发生了什么(在指针操作方面),我将非常感激

谢谢

4

2 回答 2

2

在虚拟情况下,在运行时p->vfunc()告诉p指向指针所属的类(它在内存中的任何部分)的类定义,并读取它的 vtable(或一些类似的替代实现)以找出要执行的函数称呼。

假设 vtable 如下所示:

base的虚表:

----------------
vfunc | 0x2048
---------------- 

derived1的虚表:

----------------
vfunc | 0x2096
---------------- 

vfunc然后根据 vtables 指向的位置调用基础版本或派生版本。

在非虚拟情况下,这些都不会发生,并且要调用的函数由编译器在编译时设置。在编译期间,编译器所能确定的只是它p的类型是“指向基址的指针”,并将所有p->vfunc()调用更改为指向地址0x2048

于 2012-09-22T16:42:34.820 回答
1

这里发生的情况是有两个 vtable,一个用于基础,一个用于派生。在 base 的 vtable 中,您有一个 条目vfunc(),而在 derived 的 vtable 中也有一个条目,诀窍是,当derived 采用它的父级 vtable 时,它​​会查看它是否覆盖了它的任何父级虚拟方法,并且确实如此。所以它取代了那里的东西,所以它现在指向它自己的版本。

换句话说,当您调用vfunc()base 类型的实例时,您会检查包含指向 bases 的指针的基本 vtable vfunc()。当您通过派生的实例调用它时,您会找到指向派生的指针vfunc()

如果删除 virtual 关键字,编译器将不再寻找虚函数,因此它不是基于对象的运行时类型,而是基于它的静态/编译时类型。

换句话说,如果没有 virtual 关键字,编译器会查看指针的类型,它是基数,所以它每次都调用基数版本。

于 2012-09-22T16:42:05.663 回答