cpp - vtable 指针是否在 construction/destruction 期间被更改
cpp - is vtable pointer being altered during construction/destruction
因此,在基 class 的 ctor/dtor
中使用派生 class 并调用成员函数(包括虚拟函数)时,是否通过 this
指针与否,都会调用相关class的函数。
怎么会?对象的 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;
}
在ctors/dtors中调用了created/destructed对象的成员函数
对象是它的类型......直到它不是。
根据 C++ 的规则,对象的构造函数按特定顺序调用。因为派生 class 实例 始终是 基础 class 实例,所以需要在派生 [=] 之前调用基础 class 实例构造函数41=]实例.
但如果是这样的话,那么调用基 class 构造函数时的对象是什么?派生的 class 构造函数甚至还没有 启动 ,因此将它视为派生的 class 实例是没有意义的。
所以不是。
因此,在基 class 构造函数在派生 class 的初始化中执行期间,所有 virtual
调用都像 class 是:基础 class 实例。它还不是派生class类型的实例,所以你不能还调用任何派生class的成员。
我的意思是,是的,您可以 static_cast
到派生的 class 实例,但是使用这样的指针会产生未定义的行为,因为您正在访问派生的对象 class 在该类型被初始化之前输入。
相反的情况发生在析构函数中。派生的 class 析构函数首先被调用,然后是基础 classes。但是在派生的 class 析构函数完成后...对象 不再是 派生的 class 实例。因此,在基础 class 析构函数中的任何 virtual
调用都会转到基础 class 方法。
在 vtable-based 实现中,此行为是通过在初始化期间的各个点更改 constructors/destructors 之间的 vtable 指针来实现的。 base class 构造函数将 vtable 设置为指向 base class vtable。当派生的 class 析构函数启动时,它将 vtable 设置为指向派生的 class vtable.
因此,在基 class 的 ctor/dtor
中使用派生 class 并调用成员函数(包括虚拟函数)时,是否通过 this
指针与否,都会调用相关class的函数。
怎么会?对象的 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;
}
在ctors/dtors中调用了created/destructed对象的成员函数
对象是它的类型......直到它不是。
根据 C++ 的规则,对象的构造函数按特定顺序调用。因为派生 class 实例 始终是 基础 class 实例,所以需要在派生 [=] 之前调用基础 class 实例构造函数41=]实例.
但如果是这样的话,那么调用基 class 构造函数时的对象是什么?派生的 class 构造函数甚至还没有 启动 ,因此将它视为派生的 class 实例是没有意义的。
所以不是。
因此,在基 class 构造函数在派生 class 的初始化中执行期间,所有 virtual
调用都像 class 是:基础 class 实例。它还不是派生class类型的实例,所以你不能还调用任何派生class的成员。
我的意思是,是的,您可以 static_cast
到派生的 class 实例,但是使用这样的指针会产生未定义的行为,因为您正在访问派生的对象 class 在该类型被初始化之前输入。
相反的情况发生在析构函数中。派生的 class 析构函数首先被调用,然后是基础 classes。但是在派生的 class 析构函数完成后...对象 不再是 派生的 class 实例。因此,在基础 class 析构函数中的任何 virtual
调用都会转到基础 class 方法。
在 vtable-based 实现中,此行为是通过在初始化期间的各个点更改 constructors/destructors 之间的 vtable 指针来实现的。 base class 构造函数将 vtable 设置为指向 base class vtable。当派生的 class 析构函数启动时,它将 vtable 设置为指向派生的 class vtable.