STL设计之EBO(空基类优化)
EBO简称Empty Base Optimization。
STL设计之EBO(空基类优化)
0.导语
EBO简称Empty Base Optimization。
本节从空类开始,到STL内部,到测试,再到我们自己实现一个EBO,对比性能,最后再测试,总结。
1.空类
定义一个空类:没有成员变量,没有继承,没有数据元素的类。
class Empty{
public:
void print() {
std::cout<<"I am Empty class"<<std::endl;
}
};
由于它是空的,所以这个sizeof是多少呢?
std::cout<<sizeof(Empty)<<std::endl; //1
结果是1,它是空的怎么不是0呢?
因为空类同样可以被实例化,每个实例在内存中都有一个独一无二的地址,为了达到这个目的,编译器往往会给一个空类隐含的加一个字节,这样空类在实例化后在内存得到了独一无二的地址.所以上述大小为1.
根据上面的回答,估计大家或引出另一个问题:为什么两个不同对象的地址应该不同?
现在有下面这个例子:
class Empty{
public:
void print() {
std::cout<<"I am Empty class"<<std::endl;
}
};
template < typename T >
bool isSame( T const & t1, T const & t2 )
{
return &t1 == &t2;
}
我们来测试一下:
int main() {
Empty a,b;
assert(!isSame(a,b)); // 编译通过,a与b的地址不同
Empty *p=new Empty;
Empty *q=new Empty;
assert(!isSame(p,q)); // 编译通过,a与b的地址不同
return 0;
}
上面测试了,两个不同对象地址是不同的,考虑下面场景:
Empty a,b;
// 在同一地址具有两个对象将意味着在使用指针引用它们时将无法区分这两个对象。
Empty *p1=&a;
p1->print();
此时会发现,如果a的地址与b的地址一样,那么在同一地址具有两个对象将意味着在使用指针引用它们时将无法区分这两个对象。因此两个不同对象的地址不同。
2.空基类优化
现在对比一下下面两个用法,第一种,一个类中包含了两一个类作为成员,然后通过这个来获得被包含类的功能。
class notEbo {
int i;
Empty e;
// do other things
};
另一种直接采用继承的方式来获得基类的成员函数及其他功能等等。
class ebo:public Empty {
int i;
// do other things
};
接下来做个测试:
std::cout<<sizeof(notEbo)<<std::endl;
std::cout<<sizeof(ebo)<<std::endl;
输出:
8
4
第一种,会因为字节对齐,将其原来只占1字节,进行扩充到4的倍数,最后就是8字节。
对比这两个发现,第二种通过继承方式来获得基类的功能,并没有产生额外大小的优化称之为EBO(空基类优化)。
接下来,我们回到STL源码中,看看其中的使用!
3.STL中的EBO世界
不管是deque、rb_tree、list等容器,都离不开内存管理,在这几个容器中都包含了相应的内存管理,并通过_xx_impl来继承下面这几个类:
std::allocator<_Tp>
__gnu_cxx::bitmap_allocator<_Tp>
__gnu_cxx::bitmap_allocator<_Tp>
__gnu_cxx::__mt_alloc<_Tp>
__gnu_cxx::__pool_alloc<_Tp>
__gnu_cxx::malloc_allocator<_Tp>