1
#include "stdafx.h"
#include <iostream>
using namespace std;

class ClassA
{
    protected:
       int width, height;
    public:
       void set_values(int x, int y)
       {
         width = x;
         height = y;
       }
};

class ClassB : virtual public ClassA
{
   //12(int + int + vptr)
};

class ClassC : virtual public ClassA
{
  //12(int + int + vptr)
};

class ClassD : public ClassB, public ClassC
{
};

int main()
{
  ClassA A;
  ClassB B;
  ClassC C;
  ClassD D;
  cout << "size = " << sizeof(A) << endl;
  cout << "size = " << sizeof(B) << endl;
  cout << "size = " << sizeof(C) << endl;
  cout << "size = " << sizeof(D) << endl;
  return 0;
}

我得到的输出是:

size of ClassA = 8
size of ClassB = 12
size of ClassC = 12
size of ClassD = 16

在上面的代码中,为什么 ClassD 的输出是 16。请清楚地解释一下这个虚拟继承是如何工作的。

4

3 回答 3

3

虚拟继承意味着虚拟基类只存在一次而不是多次。这就是为什么 8 个字节ClassA只出现ClassD一次的原因。虚拟继承本身需要一定的开销,因此您会得到一个额外的指针。C++ 标准未指定确切的实现以及因此的确切开销,并且可能会因您正在创建的层次结构而异。

于 2013-03-11T09:45:20.227 回答
2

当 ClassD 继承 ClassB 和 ClassC 时,将有两个 vptr(一个来自 B,一个来自 C)。Scott Meyers 的“更有效的 C++”,第 24 项(各种语言特性的成本)中描述了这种确切的情况。

于 2013-03-11T09:44:05.390 回答
0

虚拟基类实现

虚拟基类与虚拟函数完全一样:它们的地址(或相对地址,也就是偏移量)在编译时是未知的:

void f(ClassB *pb) {
    ClassA *pa = pb;
}

在这里,编译器必须计算ClassA基础子对象与ClassB子对象(或主要是派生对象)的偏移量。一些编译器只是在里面有一个指向它的指针ClassB;其他人使用 vtable,就像虚拟功能一样。

在这两种情况下,开销ClassB都是一个指针。

类似ClassC,但 vptr 将指向ClassCvtable,而不是ClassBvtable。

因此一个ClassD对象将包含(这不是一个有序列表):

  • 单个ClassA子对象
  • 一个ClassB主题
  • 一个ClassC主题

所以ClassD有两个继承的 vptr: fromClassBClassC. 在一个ClassD对象中,两个 vptr 都将指向某个 ClassDvtable,但同一个ClassDvtable:

  • a ClassBsubject指向ClassB-in-ClassD vtable,表示ClassAbase与ClassBbase的相对位置
  • a ClassCsubject指向ClassC-in-ClassD vtable,表示ClassAbase与ClassCbase的相对位置

可能的优化

我想你的问题是:我们需要两个不同的 vptr 吗?

从技术上讲,有时可以通过覆盖基类子对象来优化类的大小。这是技术上可行的情况之一:

覆盖(或统一)意味着两者ClassBClassC将共享相同的 vptr:给定d一个实例ClassD: &d.ClassB::vptr == &d.ClassC::vptrsod.ClassB::vptr == d.ClassC::vptr但是d.ClassB::vptr == &ClassC_in_ClassD_vtabled.ClassC::vptr == &ClassC_in_ClassD_vtable, soClassB_in_ClassD_vtable必须与 统一ClassC_in_ClassD_vtable。在这种特殊情况下,两者ClassB_in_ClassD_vtableClassC_in_ClassD_vtable仅用于描述ClassA子对象的偏移量;如果ClassBClassC子对象在 中统一ClassD,那么这些偏移量也是统一的,因此 vtable 的统一是可能的。

请注意,这仅在此处是可能的,因为它们具有完全的相似性。如果ClassBClassC被修改为在每个中添加一个虚函数,例如这些虚函数不等价(因此不可统一),则 vtable 统一是不可能的。

结论

这种优化只有在像这样的非常简单的情况下才有可能。这些情况不是 C++ 编程的典型情况:人们通常将虚拟基类与虚拟函数结合使用。空基类优化很有用,因为许多 C++ 习惯用法使用没有数据成员或虚函数的基类。OTOH,针对虚拟基类的特殊用途的微小(一个 vptr)空间优化似乎对现实世界的程序没有用。

于 2013-06-16T01:54:08.737 回答