cpp - vtable 指针在构造/销毁期间是否被更改

Ant*_*kov 3 c++ vtable

因此,当在ctor/dtor基类中使用派生类并调用成员函数(包括虚拟)时,无论this是否通过指针,都会调用相关类的函数。

怎么会?vtable在此过程中对象的指针是否以某种方式被更改?因为,据我所知,vtable除非使用多重继承,否则对象中通常只有一个指针。


我有以下代码来举例说明我的意思:

#include <stdio.h>


class B {
    public:
    B()
        { printf("B constructor!\n"); f(); g(); }
    virtual ~B()
        { printf("B destructor!\n");  f(); g(); }
    virtual void f() 
        { printf("f() in B!\n"); }
    void g() 
        { printf("g() in B!\n"); }
    void h() 
        { printf("h() in B!\n"); }
};

class D : public B {
    public:
    D()
        { printf("D constructor!\n"); f(); g(); }
    virtual ~D()
        { printf("D destructor!\n");  f(); g(); }
    virtual void f() 
        { printf("f() in D!\n"); }
    void g() 
        { printf("g() in D!\n"); h(); }
};

int main()
{
    D *d = new D;
    B *b = d;
    delete b;
}
Run Code Online (Sandbox Code Playgroud)

在ctors/dtors中,调用创建/销毁对象的成员函数。

Nic*_*las 13

对象就是它的类型……直到它不是。

根据 C++ 的规则,对象的构造函数按特定顺序调用。由于派生类实例始终是基类实例,因此需要在派生类实例之前调用基类实例构造函数。

但如果是这样的话,那么调用基类构造函数时对象是什么?派生类构造函数甚至还没有启动,因此将其视为派生类实例还没有意义。

所以事实并非如此。

因此,在派生类初始化期间执行基类构造函数期间,所有virtual调用的工作方式就好像该类就是:基类实例。它还不是派生类类型的实例,因此您不能调用派生类的任何成员。

我的意思是,是的,您可以static_cast访问派生类实例,但使用这样的指针会产生未定义的行为,因为您在初始化派生类类型之前访问该类型的对象。

相反的情况发生在析构函数中。首先调用派生类的析构函数,然后调用基类。但是派生类析构函数完成后......该对象不再是派生类实例。因此,virtual基类析构函数中的任何调用都会转到基类方法。

在基于 vtable 的实现中,此行为是通过在初始化期间的各个点更改构造函数/析构函数之间的 vtable 指针来实现的。基类构造函数将 vtable 设置为指向基类 vtable。当派生类析构函数启动时,它会将 vtable 设置为指向派生类 vtable。