我正在阅读C++对象模型中的。在第1.3节中


Bear b;
ZooAnimal za = b;

// ZooAnimal::rotate() invoked
za.rotate();



所以我在下面写了测试代码:
#include <stdio.h>
class Base{
public:
    virtual void vfunc() { puts("Base::vfunc()"); }
};
class Derived: public Base
{
public:
    virtual void vfunc() { puts("Derived::vfunc()"); }
};
#include <string.h>

int main()
{
    Derived d;
    Base b_assign = d;
    Base b_memcpy;
    memcpy(&b_memcpy, &d, sizeof(Base));

    b_assign.vfunc();
    b_memcpy.vfunc();

    printf("sizeof Base : %d\n", sizeof(Base));

    Base &b_ref = d;
    b_ref.vfunc();

    printf("b_assign: %x; b_memcpy: %x; b_ref: %x\n",
        *(int *)&b_assign,
        *(int *)&b_memcpy,
        *(int *)&b_ref);
    return 0;
}

result
Base::vfunc()
Base::vfunc()
sizeof Base : 4
Derived::vfunc()
b_assign: 80487b4; b_memcpy: 8048780; b_ref: 8048780

我的问题是为什么b_memcpy仍称Base::vfunc()

最佳答案

您正在执行的操作在C++语言中是非法的,这意味着b_memcpy对象的行为未定义。后者意味着任何行为都是“正确的”,您的期望是完全没有根据的。尝试分析未定义的行为没有太多意义-不应遵循任何逻辑。

实际上,您对memcpy的操作很可能确实将Derived的虚拟表指针复制到了b_memcpy对象。您对b_ref的实验证实了这一点。但是,当通过直接对象调用虚拟方法时(如b_memcpy.vfunc()调用的情况),大多数实现会优化对虚拟表的访问,并执行对目标函数的直接(非虚拟)调用。该语言的形式规则规定,没有任何法律行动可以进行b_memcpy.vfunc()调用以调度到Base::vfunc()以外的任何其他对象,这就是为什么编译器可以用直接调用Base::vfunc()来安全地替换此调用的原因。这就是为什么任何虚拟表操作通常不会对b_memcpy.vfunc()调用产生影响的原因。

10-08 00:33