引子
几个简单类的大小
空类
class Empty {};
int main() {
cout << sizeof(Empty) << endl; //1
}
空类,1个字节,不表示任何内容。
普通继承
class A {
public:
int m_A;
static int s_iA;
void funA();
};
class B:A {
short m_B;
void funB();
};
int main() {
cout << sizeof(A) << endl; //4
cout << sizeof(B) << endl;//8,继承m_A4字节,自己的short m_B2字节,对齐变4字节,一共8字节
}
类大小的计算:
- 只有类成员变量占内存,普通函数不占用内存
- static变量属于类,不属于对象,也不占用内存
sizeof(A) 4字节,就是int型变量4个字节,static变量不占空间。
sizeof(B) 8个字节,继承类A的int型变量,占用4个字节
自己的short占用两个字节,但是由于内存对齐,short也占用了4字节。
一共8字节。
包含虚函数的继承
class A {
public:
int m_A;
static int s_iA;
virtual void funA();
};
class B:A {
short m_B;
void funB();
};
int main() {
cout << sizeof(A) << endl; //16
cout << sizeof(B) << endl;//24
}
代码和上面的代码相比,只是在将类A中的函数变成了virtual 。
有虚方法的类对象中会有一个虚指针(vfptr),指向虚表。指针大小取决于操作系统。32位系统指针4字节,64位系统8字节。
即使有多个虚函数,也是写在一张虚表内的,所以只需要一个虚表指针。
这里是64位系统。
sizeof(A) 为16,包含一个虚指针(8字节)+int型变量(4字节,对齐成8字节)。
sizeof(B) 为24,包含一个虚指针(8字节)+继承的int型变量+自己的int型变量。
虚继承
虚继承解决了菱形继承的问题。代码如下
class A {
public:
int m_A;
virtual void funA();
};
class B:virtual public A {
int m_B;
void funB();
};
class C :virtual public A {
int m_C;
void funB();
};
class D :public B, C {
int m_D;
};
int main() {
cout << sizeof(A) << endl; //16
cout << sizeof(B) << endl; //32
cout << sizeof(C) << endl; //32
cout << sizeof(D) << endl; //56
}
菱形继承的好处是消除了二义性,减少了内存浪费。
对虚继承的类A,C对象中只会有一份A的数据。
虚继承借助了虚基类表指针(vbptr),在虚继承的类中,虚基类表指针指向了一个偏移量。当前地址加上偏移量就可以得到A数据所在的地址。
sizeof(A) 还是16,这是基类。
sizeof(B)和sizeof©大小相同,都是原来24的大小上加一个vbptr变成了32。
sizeof(D) = sizeof(m_A) //仅一份
+ sizeof(m_B) + sizeof(m_C)
+ sizeof(m_D)
+ sizeof(vbptr)//B虚继承,生成的虚基类指针
+ sizeof(vbptr)//C虚继承,生成的虚基类指针
+ sizeof(vfptr) //指向虚表
参考极大帮助理解~必看!
通过vbptr寻找A成员变量的具体过程
虚继承实现原理,内存示意图