1

在对象切片中,当派生类对象被复制到基类对象时,派生类的 _vptr 是否也像 Base 类的其他成员一样被复制到基类的 _vptr?如果不是,为什么?

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
4

2 回答 2

2

vptr 不会被复制。让我们试着推理一下为什么

查看您的主要功能:

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

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

Base& operator=(const Base& other)

并且它作为 Base& 传递给 objD。因此,您的问题变成了“赋值运算符是否复制 vptr”?答案是不”。默认赋值运算符仅复制类上的字段。

然后你可能会问,“为什么它不复制 vptr 呢?” 原因是,如果它复制了 vptr,Base 类上的方法最终将使用 Derived 类上实现的方法。但是,总的来说,这些方法可以使用仅存在于 Derived 类中的数据成员(并且不存在于 Base 类中)。因此调用这些方法是没有意义的(数据在逻辑上不存在于 Base 类中),因此语言正确地选择不这样做。

主要问题是,当您将派生类分配给基类时,您分配给的变量仅包含基类的字段,因此派生类中不在基类中的字段不是复制。因此,派生类的方法在基类上调用时没有意义。

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

这就是为什么在 C++ 中,要进行多态性(通过虚方法),您需要使用引用或指针。请参阅此处进行类似的讨论: 为什么多态性在没有指针/引用的情况下不起作用?

于 2020-07-11T19:26:50.300 回答
-1

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

请注意,在许多情况下,涉及多个简单继承(单个非虚拟继承)的类具有多个 vptr。

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

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

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

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++ 类型系统和声明对象的不变量。编译器不知道要销毁哪个类、调用哪个虚函数、基类在哪里等等。它将彻底改变 C++ 的工作方式。甚至很难想象与更改现有对象的类型相关的语义。

于 2019-03-08T20:13:44.967 回答