虛函數是C++實現多態的工具,在運行時根據虛表決定調用合適的函數。這被稱作動態分發。虛函數很好的實現了多態的要求,但是在運行時引入了一些開銷,包括:
- 對每一個虛函數的調用都需要額外的指針尋址
- 虛函數通常不能被inline,當虛函數都是小函數時會有比較大的性能損失
- 每個對象都需要有一個額外的指針指向虛表
所以如果是一個對性能要求非常嚴格的場合,我們就需要用別的方式來實現分發,這就是今天這篇博客的主角CRTP。
CRTP通過模板實現了靜態分發,會帶來很多性能的好處。可以參見The cost of dynamic (virtual calls) vs. static (CRTP) dispatch in C++看一下性能比較。
下面簡單介紹一下怎么實現CRTP。
首先看我們的父類:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
template<typename Derived> class Parent { public: void SayHi() { static_cast<Derived*>(this)->SayHiImpl(); } private: void SayHiImpl() // no need if no need the default implementation { cout << "hi, i'm default!" << endl; } }; |
它是一個模板類,它有一個需要接口函數是SayHi
。它有一個默認實現在SayHiImpl
中。
再來看看它的子類:
1 2 3 4 5 6 7 8 9 10 11 12 |
class ChildA :public Parent<ChildA> { public: void SayHiImpl() { cout << "hi, i'm child A!" << endl; } }; class ChildB :public Parent<ChildB> { }; |
我們可以看到ChildA
和ChildB
繼承自這個模板類,同時ChildA
有自己的實現。
在寫一個普通的用虛函數實現分發的類:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
class ParentB { public: void virtual SayHi() { cout << "hi, i'm default!" << endl; } }; class ChildC : public ParentB { public: void SayHi() { cout << "hi, i'm ChildC!" << endl; } }; class ChildD : public ParentB { }; |
然后是調用這兩個父類的函數:
1 2 3 4 5 6 7 8 9 |
template<typename Derived> void CRTP(Parent<Derived>& p) { p.SayHi(); } void Dynamic(ParentB& p) { p.SayHi(); } |
再來看看main
函數:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
int _tmain(int argc, TCHAR* argv[]) { ChildA a; CRTP(a); cout << "size of ChildA: " << sizeof(a) << endl; ChildB b; CRTP(b); cout << "size of ChildB: " << sizeof(b) << endl; ChildC c; Dynamic(c); cout << "size of ChildC: " << sizeof(c) << endl; ChildD d; Dynamic(d); cout << "size of ChildD: " << sizeof(d) << endl; return 0; } |
如果運行這個程序,可以看到如下的輸出,可以看到CRTP可以實現和虛函數一樣的功能,但是內存大小會有很大優勢,關於對象內存可以參見我之前的博客怎么看C++對象的內存結構 和 怎么解密C++的name Mangling:
1 2 3 4 5 6 7 8 9 |
hi, i'm child A! size of ChildA: 1 hi, i'm default! size of ChildB: 1 hi, i'm ChildC! size of ChildC: 4 hi, i'm default! size of ChildD: 4 Press any key to continue . . . |
完整代碼參見gist。