虚继承中派生类的大小
size of derived class in virtual inheritance
#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。请给我解释清楚这个虚拟继承是如何工作的。
虚继承意味着虚基类只存在一次而不是多次。这就是为什么ClassA
中的8个字节只在ClassD
中出现一次。虚拟继承本身需要一定的开销,因此您会得到一个额外的指针。c++标准并没有规定具体的实现,因此具体的开销可能会根据您正在创建的层次结构而变化。
当ClassD继承ClassB和ClassC时,将会有两个vptrs(一个来自B,一个来自C)。这种确切的情况在Scott Meyers的"更有效的c++"第24项(各种语言特性的代价)中有描述。
虚拟基类实现
虚基类与虚函数完全相同:它们的地址(或相对地址,又称偏移量)在编译时是未知的:
void f(ClassB *pb) {
ClassA *pa = pb;
}
这里编译器必须计算ClassA
基子对象与ClassB
子对象(或大部分派生对象)的偏移量。有些编译器只是在ClassB
内部有一个指向它的指针;其他使用虚函数表,就像虚函数一样。
在这两种情况下,ClassB
的开销都是一个指针。
ClassC
是类似的,但是vptr将指向ClassC
虚表,而不是ClassB
虚表。
因此,ClassD
对象将包含(这不是一个有序列表):
- 单个
ClassA
子对象 - a
ClassB
subject - a
ClassC
subject
所以ClassD
有两个继承的vptr:来自ClassB
和ClassC
。在ClassD
对象中,两个vptr都指向某个 ClassD
虚表,但指向同一个ClassD
虚表:
- a
ClassB
主题指向ClassB-in-ClassD虚表,表示ClassA
基与ClassB
基 的相对位置。 - a
ClassC
主题指向ClassC-in-ClassD虚表,表示ClassA
碱基与ClassC
碱基的相对位置
我想你的问题是:我们需要两个不同的vptr吗?
从技术上讲,有时可以通过覆盖基类子对象来优化类的大小。这是技术上可行的情况之一:
重叠(或统一)意味着ClassB
和ClassC
将共享同一个vptr:给定d
一个ClassD
的实例:&d.ClassB::vptr == &d.ClassC::vptr
不等于d.ClassB::vptr == d.ClassC::vptr
,但d.ClassB::vptr == &ClassC_in_ClassD_vtable
和d.ClassC::vptr == &ClassC_in_ClassD_vtable
,所以ClassB_in_ClassD_vtable
必须与ClassC_in_ClassD_vtable
统一。在本例中,ClassB_in_ClassD_vtable
和ClassC_in_ClassD_vtable
只用于描述ClassA
子对象的偏移量;如果ClassB
和ClassC
子对象在ClassD
中是统一的,那么这些偏移量也是统一的,因此统一虚表是可能的。
注意,这只可能在这里,因为有完美的相似性。如果对ClassB
和ClassC
进行修改,在它们各自中添加一个虚函数,比如这些虚函数不相等(因此不统一),那么虚函数表就不可能统一。
这种优化只在像这样的非常简单的情况下才有可能。这些情况并不是典型的c++编程:人们通常将虚拟基类与虚拟函数结合使用。空基类优化很有用,因为许多c++习惯用法使用没有数据成员或虚函数的基类。OTOH,一个很小的(一个vptr)空间优化,用于虚拟基类的特殊用途,似乎对现实世界的程序没有用处。
- 大小虚拟继承中的派生类
- 继承时如何构建派生类的变量?
- 如何使用 C++ 中的继承函数访问派生类中的局部变量
- 派生类是从基类继承 v 指针并仅使用它,还是也有自己的 v 指针?
- "std::shared_from_this"不能由其派生类继承吗?
- 继承的数据如何在派生类中放置(排列)?
- C++具有空派生类对象的继承大小
- C++ 继承:基类中重载 operator+ 的 2 次在派生类中无法正常工作
- 如何调用继承的重载运算符<<并在派生类的输出中添加更多文本?
- 在模板派生类中继承具有类型别名的构造函数
- C++继承从基类指针访问派生类中的非虚拟函数
- C++排除通过派生类中的基类继承的类
- 派生类调用使用非继承成员的继承函数
- 多重继承中的派生类的行为类似于聚合
- 在派生类中具有相同签名但继承为受保护的函数
- 有没有办法调用基类函数,该函数在使用私有继承的派生类中被覆盖?
- 继承:动态派生类成员与静态派生类成员
- SIGSEGV 因为基/派生类的继承问题
- 从多重继承中的派生类函数调用适当的父类函数
- 继承函数是否适用于 C++ 中的基类元素或派生类元素?