一、EBO的背景
我們知道一個空的類,也就是其內部沒有非靜態數據成員,沒有虛指針(包括指向虛函數表和虛基類子對象的指針),它的大小通常為1,當然在某些對齊要求嚴格系統上可能是另一個數(通常是4),如果空類被繼承,那麼派生類的大小會怎麼樣呢?一個支持C++標准和EBO的編譯器對此會進行空基類的優化,也就是不給空的基類子對象分配空間,換句話說,空基類子對象的地址和其派生類實例的地址是相同的。從編譯器實現的角度來看,需要考慮繼承時的不同情況,下圖中P表示父類,C表示子類,圓形表示空類,矩形表示非空類。單繼承EBO情況如下圖所示
EBO-1反映的是空類派生自空基類,EBO-2反映的是非空類派生自空基類,EBO-3、EBO-4反映的是在繼承鏈中,對空基類的優化能不能傳遞到後代中。多繼承EBO如下圖所示
EBO-5反映的是空類派生自兩個空基類,EBO-6反映的是非空類派生自兩個空基類,EBO-6反映的是空類派生自一個非空基類和一個空基類,EBO-7反映的是非空類派生自一個非空基類和一個空基類。以上8種情況,不論是單繼承還是多繼承,一個完全支持EBO的編譯器就應該能把空基類部分都優化掉。
二、EBO的應用
由於空基類優化技術節省了對象不必要的空間,提高了運行效率,因此成為某些強大技術的基石,基於類型定義類如stl中的binary_function、unary_function、iterator、iterator_traits的實現復用;基於策略類如內存管理、多線程安全同步的實現復用。當某個類存在空類類型的數據成員時,也可考慮借助EBO優化對象布局,代碼描述如下
1template<typename T1,typename T2>
2class EBO
3{
4private:
5 T1 m_t1;
6 T2 m_t2;
7};
當T1和T2為空類時,可以改進如下
1template<typename T1,typename T2>
2class EBO : T1, T2
3{
4};
進一步擴展考慮,如果T1或T2為非類類型,如基本內建類型、函數指針等;或T1和T2類型相同時,則直接繼承它們會導致編譯錯誤,怎麼辦呢?這時可以添加一個中間層來解決,代碼如下
1template<typename T1,typename T2,bool isSame,bool isFirstEmpty,bool isSecondEmpty>
2class EBO_IMPL;
3
4template<typename T1,typename T2>
5class EBO_IMPL<T1,T2,false,false,false>
6{
7 T1 m_t1;
8 T2 m_t2;
9};
10
11template<typename T1,typename T2>
12class EBO_IMPL<T1,T2,false,true,true> : T1,T2
13{
14};
15
16template<typename T1,typename T2>
17class EBO_IMPL<T1,T2,false,true,false> : T1
18{
19 T2 m_t2;
20};
21
22template<typename T1,typename T2>
23class EBO_IMPL<T1,T2,false,false,true> : T2
24{
25 T1 m_t1;
26};
27
28template<typename T1,typename T2>
29class EBO_IMPL<T1,T2,true,false,false>
30{
31 T1 m_t1;
32 T2 m_t2;
33};
34
35template<typename T1,typename T2>
36class EBO_IMPL<T1,T2,true,true,true> : T1
37{
38 T2 m_t2;
39};
40
41template<typename T1,typename T2>
42class EBO : EBO_IMPL<T1,T2,boost::is_same<T1,T2>::value,boost::is_empty<T1>::value,boost::is_empty<T2>::value>
43{
44};
為了簡便實現,上面代碼直接使用了boost中的is_same,is_empty元函數來判斷類型的屬性,實際上boost中已經實現了EBO的選擇運用工具即compressed_pair類模板,研究其源碼可發現,該工具充分考慮到了T1和T2實際類型的各種情況,is_empty的判斷是運用sizeof來比較類型大小確定的。替換compressed_pair後,代碼如下
1template<typename T1,typename T2>
2class EBO: boost::compressed_pair<T1,T2>
3{
4};