虚拟 Table 和对象切片

Virtual Table and Object Slicing

在对象切片中,当派生 class 对象被复制到 Base class 对象时,Derived class 的 _vptr 是否也被复制到 Base [=17] 的 _vptr =] 喜欢 class 基地的其他成员?如果不是,为什么?

class Base{  
public :
virtual void Display(){cout<<"In Base"<<endl;}
 };
class Derived:public Base{
public:
void Display(){cout<<"In Derived"<<endl;}

};
int main()
{
Derived objD;
Base objB;
objB = objD;
objB.Display();
}

我观察到上述代码段的结果如下。

Output
In Base

所有已知的实现都使用 vptr,但细节差异很大。 vptr 是实现表示多态对象类型的一种方式 class (class 带有虚函数),有时 classes 带有虚基数 classes (非常依赖于实现)。

请注意,在许多情况下涉及的不仅仅是简单继承(单一非虚拟继承)classes 有多个 vptr。

vptr 值是构造的最派生对象类型的函数。

对象的类型在其生命周期内不能被用户改变;在构造过程中,对象的类型在构造时发生变化;在销毁期间,它会变回来。 (在构造或析构期间使用类型与对象不匹配的名称或其他表达式引用对象是非法的。)

您可以重用现有对象的 space 来构造不同类型的对象,但它不是同一个对象:

struct X {
  A a;
  B b;
  // requires: construction doesn't throw
};

void replace (X &x) {
  x.~X(); 
  B &b = *new (&x) B; // hope no exception here
  b.f(); // use as a normal B object
  x.f(); // undefined: cannot use x as a real object
  X *p = &x; // legal: x isn't used as an object, only as an address
  b.~B(); // clean up ressources if needed
  new (&x) X; // valid: &x refers to storage, as if a void*
  // x refers to a valid X object 
}

此类对象重用不会更改任何现有对象的类型:replace(x) 不仅作用于 x(它通过破坏一个对象并重建一个对象),它作用于x 的存储位置。在调用 replace(x) 之后,名称 x 可以用来引用新构造的 X 对象,它有效地获取了先前存在的 X 对象的标识。

在 C++ 中,甚至不允许对对象的任何操作(使对象保持活动状态)更改其类型。 vptr 无法在构造的对象上更改。

更改已声明对象的类型会破坏 C++ 类型系统和已声明对象的不变量。编译器不知道要销毁什么 class,要调用哪些虚函数,基础 classes 在哪里,等等。这将彻底改变 C++ 的工作方式。甚至很难想象与更改现有对象的类型相关联的语义。

未复制 vptr。让我们试着推理一下 为什么 .

查看您的主要功能:

Derived objD;
Base objB;
objB = objD;  // <-- HERE
objB.Display();

在第 3 行中,您将 objD 分配给 objB。这实际上是调用Base的赋值运算符(自动定义的):

Base& operator=(const Base& other)

它作为 Base& 传递给 objD。所以,您的问题变成了“赋值运算符是否复制了 vptr”?答案是不”。默认赋值运算符仅复制 class.

上的字段

然后您可能会问,“为什么它不也复制 vptr?”原因是,如果它复制 vptr,Base class 上的方法最终将使用 Derived class 上实现的方法。但是,总的来说,这些方法可以使用仅存在于 Derived class 上的数据成员(而不存在于 Base class 上)。因此调用这些方法是荒谬的(数据在逻辑上不存在于 Base class),因此语言选择不这样做是正确的。

主要问题是,当您将 Derived class 分配给 Base class 时,您分配给的变量仅包含 Base class 的字段,因此派生 class 中不在基础 class 中的字段不会被复制。因此,派生 class 中的方法在基 class.

上调用时没有意义

请注意,如果您要将 Base 指针或 Base 引用分配给 Derived class,则情况并非如此。在那种情况下,原始的 Derived class 实例仍然存在。它位于内存中的某个地方,并且具有所有 Base+Derived class 字段。因此,在该实例上调用的 Derived class 的方法将可以访问这些字段,因此能够调用这些方法仍然有意义。

这就是为什么在 C++ 中,要实现多态性(通过虚拟方法),您需要使用引用或指针。有关类似的讨论,请参见此处: Why doesn't polymorphism work without pointers/references?