空基类优化
empty base class optimization(EBCO),
#include <iostream>
class Empty {
public:
using IntType = int; // 类型别名成员不会使类非空
};
class EmptyTwo : public Empty {};
class EmptyThree : public EmptyTwo {};
int main() {
std::cout << "sizeof(Empty) = " << sizeof(Empty) << std::endl;
std::cout << "sizeof(EmptyTwo) = " << sizeof(EmptyTwo) << std::endl;
std::cout << "sizeof(EmptyThree) = " << sizeof(EmptyThree) << std::endl;
return 0;
}
如果编译器会实现了EBCO,会发现继承基类的派生类和基类具有同样的大小,且继承优化后的空派生类,大小依旧不变。
空基类优化(EBCO,Empty Base Class Optimization)是模板库中的一项重要优化手段。空基类优化是指当一个类作为基类且其内部没有任何非静态数据成员或其他需要运行时内存分配的内容时,编译器可以优化其布局,使得多个空基类不会重复占用存储空间。这样做的结果是可以节省内存,特别是当用户定义的类派生自多个这样的空基类时,每个空基类都可以在对象内存布局中占据零字节的位置。
当知道模板参数仅会被类类型替换,且类模板中有其他可用成员时,可以设计一种更为实用的方法。这种方法的主要思想是利用空基类优化(EBCO)来“合并”可能为空的类型参数和其他成员。具体而言,就是说在编写代码时,不是独立声明一个可能会成为空类型的模板参数类型的数据成员,而是将它与另一个数据成员通过继承的方式合并到同一个复合类中,以利用EBCO节省空间。
例如,原本可能是这样编写代码:
template<typename CustomClass>
class Optimizable {
private:
CustomClass info; // 这个信息类可能为空
void* storage;
...
};
为了避免潜在的空类型CustomClass
浪费空间,可以改为使用BaseMemberPair
模板类,如下所示:
template<typename CustomClass>
class Optimizable {
private:
BaseMemberPair<CustomClass, void*> info_and_storage;
...
};
这里BaseMemberPair
模板类是根据EBCO原则设计的,它可以有效地整合CustomClass
和void* storage
这两个成员,当CustomClass
为空类型时,依然可以确保整体结构紧凑,避免无谓的空间开销。
The Curiously Recurring Template Pattern (CRTP)
奇怪的循环模板模式,The Curiously Recurring Template Pattern (CRTP), 将派生类作为模板参数传递给自己的基类之一。
template<typename T>
class CuriousBase {
};
// 将派生类自己作为参数传入给基类
class Curious : public CuriousBase<Curious> {};
简单应用:跟踪一个类目前还存活多少个对象:
#include <iostream>
template<typename CountedType>
class ObjectCounter {
private:
// inline 关键字和 static 一起使用是为了确保在多文件项目中,
// 无论包含该变量声明的头文件被多少个源文件包含,
// 整个程序都只会有一个 count 变量实例,并且能正确地在整个程序范围内共享和更新。
// 同时,这也是一种常用的创建跨编译单元全局唯一静态变量的方法。
inline static std::size_t count = 0;
protected:
ObjectCounter() { ++count; }
ObjectCounter(ObjectCounter<CountedType> const& /*unused*/) {++count;}
ObjectCounter (ObjectCounter<CountedType> && /*unused*/) noexcept {++count;}
~ObjectCounter() {--count;}
public:
static std::size_t live() { return count; }
};
// CRTP, 将派生类作为模板参数传递给自己的基类之一。
template<typename T>
class MyClass : public ObjectCounter<MyClass<T>> {};
int main() {
auto *m1 = new MyClass<int>;
auto *m2 = new MyClass<int>;
MyClass<char> c1;
std::cout << "MyClass<int> live: " << MyClass<int>::live() << std::endl;
delete m1;
std::cout << "MyClass<int> live: " << MyClass<int>::live() << std::endl;
delete m2;
std::cout << "MyClass<int> live: " << MyClass<int>::live() << std::endl;
std::cout << "c1 live: " << c1.live() << std::endl;
return 0;
}