前言
本文的目的在于用尽可能少的字数及版面下,直观地展示空基类优化(EBO/EBCO)的作用。
为什么要使用空基类优化
C++标准规定对象的大小不能为0。标准要求每个对象在内存中必须占有一定的空间,这意味着即使这个类没有任何成员,sizeof(A)
也不会为0,那么:
class A
{
/* some functions here */
};
class B
{
A a;
int i;
}; // sizeof(B) > sizeof(int)
类型A
虽然没有任何数据成员,但是它的对象仍然占有一定空间,但是,对于只调用类型A
的函数的使用者而言,这个空间是没有用处的。而且因为内存对齐的原因,类型B
的实际大小会对齐到两个int
(甚至更多),因此我们需要想办法去除掉这一“多余”的空间。
使用空基类优化
不考虑多继承(详见延伸)的情况下,通过继承空类,我们就能避免这个空类占用空间:
class A {};
class B {
struct A_ : public A {
int i;
};
A_ a_;
}
此时sizeof(A_)=sizeof(int)
,此时派生类A_
中类型A
的对象不占内存空间。
上面的代码中我们可以看出,空基类优化的使用场景之一就是通过非空类型继承获得空基类的函数,利用派生类的对象来调用这些函数
举个标准库的例子:
class basic_string {
struct _Alloc_hider : allocator_type {
/* ctors... */
pointer _M_p;
};
_Alloc_hider _M_dataplus;
/*
如果不用空基类优化,就是:
pointer _M_p;
allocator_type _M_alloc;
那么sizeof(basic_string)就会大于等于(考虑到对齐)
sizeof(pointer) + sizeof(alloctor_type) + ...
使用空基类优化之后,就少了这一个sizeof(alloctor_type)或者对齐字节的大小
*/
};
就是利用_Alloc_hider
来继承allocator_type
这一可能的空基类,来优化基类的内存占用,从而避免内存浪费。为了让_Alloc_hider
非空而又不增大basic_string
的体积,就将存储字符串地址的_M_p
放在了basic_string::_Alloc_hider
而非basic_string
中。
相关延伸
由于本文只是简单介绍和展示EBO,并未展示其限制或更多应用,因此列出延伸阅读供更多学习和参考:
C++编程技巧: EBCO,知乎#利用EBCO来优化Tuple,#不满足EBCO原则的空类继承
C/C++编程:空基类优化,CSDN
cppreference.com/cpp/language/ebo