關鍵字:static_cast,dynamic_cast,fast_dynamic_cast,VS 2015。
OS:Window 10。
C++類之間類型轉換有:static_cast、dynamic_cast、reinterpret_cast、和const_cast。
static_cast - 編譯時類型檢查。如果沒有繼承關系的類之間轉換編譯不通過。優點是快,缺點是從父類轉換到子類不安全的。
dynamic_cast - 運行時類型檢查。可以父子之間轉換,也可以兄弟之間轉換。優點是安全,缺點是運行時效率低。
reinterpret_cast - 強制轉換。最不安全。只有特定場合才能使用。
const_cast - const類型和非const類型互轉。
一般從父類轉換到子類,或兄弟類之間轉換使用dynamic_cast是正確的選擇。但是對於大型應用程序的底層開發,dynamic_cast的性能問題就暴露出來了,使用static_cast又不能保證安全,這時就需要自己實現一套高效安全的運行時動態類型轉換。
基於虛函數+類型檢查的類型轉換
1. 為每個類實現classType和queryObject方法。運行時,通過虛函數queryObject調用以及在queryObject里面檢查classType來找到合適的對象。具體實現如下:
class A { public: static const char* classType(); virtual void* queryObject(const char* classType) const; }; const char* A::classType() { static const char* s_classType = "A"; return s_classType; } void* A::queryObject(const char* classType) const { if (classType == A::classType()) return const_cast<A*>(this); return nullptr; } class B { public: static const char* classType(); virtual void* queryObject(const char* classType) const; }; const char* B::classType() { static const char* s_classType = "B"; return s_classType; } void* B::queryObject(const char* classType) const { if (classType == B::classType()) return const_cast<B*>(this); return nullptr; } class C : public A, public B { public: static const char* classType(); void* queryObject(const char* classType) const override; }; const char* C::classType() { static const char* s_classType = "C"; return s_classType; } void* C::queryObject(const char* classType) const { if (classType == C::classType()) return const_cast<C*>(this); if (void* res = A::queryObject(classType)) return res; if (void* res = B::queryObject(classType)) return res; return nullptr; } class D : public A { public: static const char* classType(); void* queryObject(const char* classType) const override; }; const char* D::classType() { static const char* s_classType = "D"; return s_classType; } void* D::queryObject(const char* classType) const { if (classType == D::classType()) return const_cast<D*>(this); return A::queryObject(classType); } template <typename To, typename From> To* fast_dynamic_cast(const From* from) { if (!from) return nullptr; return static_cast<To*>(from->queryObject(To::classType())); }
2. new對象C,用指針A指向C對象。C繼承於A和B。
A* a = new C(); cout << "A* a = new C();" << endl;
3. 測試類型轉換從A到C,計時和檢驗轉換結果。從測試結果看,dyanmic_cast、static_cast、fast_dynamic_cast結果都正確,static_cast效率最高,fast_dynamic_cast其次,dyanmic_cast效率最差。
cout << "===== cast from pointer A to pointer C, should be not null =====" << endl; C* c = nullptr; start = clock(); for (int i = 0; i < count; i++) { c = dynamic_cast<C*>(a); } stop = clock(); cout << "dynamic_cast from A to C: " << c << ", " << "Time: " << stop - start << endl; c = nullptr; start = clock(); for (int i = 0; i < count; i++) { c = static_cast<C*>(a); } stop = clock(); cout << "static_cast from A to C: " << c << ", " << "Time: " << stop - start << endl; c = nullptr; start = clock(); for (int i = 0; i < count; i++) { c = fast_dynamic_cast<C>(a); } stop = clock(); cout << "fast_dynamic_cast from A to C: " << c << ", " << "Time: " << stop - start << endl;
測試結果:
===== cast from pointer A to pointer C, should be not null ===== dynamic_cast from A to C: 00000202D48C9FE0, Time: 2227 static_cast from A to C: 00000202D48C9FE0, Time: 0 fast_dynamic_cast from A to C: 00000202D48C9FE0, Time: 199
4. 測試類型轉換從A到B,計時和檢驗轉換結果。從測試結果看,static_cast編譯不通過,dyanmic_cast、fast_dynamic_cast結果都正確,fast_dynamic_cast效率比dyanmic_cast高。
cout << "\n===== cast from pointer A to pointer B, should be not null =====" << endl; B* b = nullptr; start = clock(); for (int i = 0; i < count; i++) { b = dynamic_cast<B*>(a); } stop = clock(); cout << "dynamic_cast from A to B: " << b << ", " << "Time: " << stop - start << endl; b = nullptr; start = clock(); for (int i = 0; i < count; i++) { //b = static_cast<B*>(a); //compiler error } stop = clock(); cout << "static_cast from A to B: " << "compiler error" << endl; b = nullptr; start = clock(); for (int i = 0; i < count; i++) { b = fast_dynamic_cast<B>(a); } stop = clock(); cout << "fast_dynamic_cast from A to B: " << b << ", " << "Time: " << stop - start << endl;
測試結果:
===== cast from pointer A to pointer B, should be not null ===== dynamic_cast from A to B: 000001D65F2FA308, Time: 2927 static_cast from A to B: compiler error fast_dynamic_cast from A to B: 000001D65F2FA308, Time: 208
5. 測試類型轉換從A到D,計時和檢驗轉換結果。從測試結果看,static_cast結果不正確,應為空指針,dyanmic_cast、fast_dynamic_cast結果都正確,fast_dynamic_cast效率比dyanmic_cast高。
cout << "\n===== cast from pointer A to pointer D, should be null =====" << endl; D* d = nullptr; start = clock(); for (int i = 0; i < count; i++) { d = dynamic_cast<D*>(a); } stop = clock(); cout << "dynamic_cast from A to D: " << d << ", " << "Time: " << stop - start << endl; d = nullptr; start = clock(); for (int i = 0; i < count; i++) { d = static_cast<D*>(a); } stop = clock(); cout << "static_cast from A to D: " << d << ", " << "Time: " << stop - start << endl; d = nullptr; start = clock(); for (int i = 0; i < count; i++) { d = fast_dynamic_cast<D>(a); } stop = clock(); cout << "fast_dynamic_cast from A to D: " << d << ", " << "Time: " << stop - start << endl;
測試結果:
===== cast from pointer A to pointer D, should be null ===== dynamic_cast from A to D: 0000000000000000, Time: 3534 static_cast from A to D: 0000026050C6D310, Time: 0 fast_dynamic_cast from A to D: 0000000000000000, Time: 227
總結:根據以上測試結果,fast_dynamic_cast和dynamic_cast行為完全一致,並且效率上fast_dynamic_cast比dynamic_cast快10倍以上,在追求性能的底層開發完全可以用fast_dynamic_cast代替dynamic_cast。
附完整代碼:

#include "stdafx.h" #include <string> #include <time.h> #include <iostream> using namespace std; namespace { class A { public: static const char* classType(); virtual void* queryObject(const char* classType) const; template <typename From> static A* queryObject(const From* from) { if (!from) return nullptr; return static_cast<A*>(from->queryObject(A::classType())); } }; const char* A::classType() { static const char* s_classType = "A"; return s_classType; } void* A::queryObject(const char* classType) const { if (classType == A::classType()) return const_cast<A*>(this); return nullptr; } class B { public: static const char* classType(); virtual void* queryObject(const char* classType) const; template <typename From> static B* queryObject(const From* from) { if (!from) return nullptr; return static_cast<B*>(from->queryObject(B::classType())); } }; const char* B::classType() { static const char* s_classType = "B"; return s_classType; } void* B::queryObject(const char* classType) const { if (classType == B::classType()) return const_cast<B*>(this); return nullptr; } class C : public A, public B { public: static const char* classType(); void* queryObject(const char* classType) const override; template <typename From> static C* queryObject(const From* from) { if (!from) return nullptr; return static_cast<C*>(from->queryObject(C::classType())); } }; const char* C::classType() { static const char* s_classType = "C"; return s_classType; } void* C::queryObject(const char* classType) const { if (classType == C::classType()) return const_cast<C*>(this); if (void* res = A::queryObject(classType)) return res; if (void* res = B::queryObject(classType)) return res; return nullptr; } class D : public A { public: static const char* classType(); void* queryObject(const char* classType) const override; template <typename From> static D* queryObject(const From* from) { if (!from) return nullptr; return static_cast<D*>(from->queryObject(D::classType())); } }; const char* D::classType() { static const char* s_classType = "D"; return s_classType; } void* D::queryObject(const char* classType) const { if (classType == D::classType()) return const_cast<D*>(this); return A::queryObject(classType); } template <typename To, typename From> To* fast_dynamic_cast(const From* from) { return To::queryObject(from); } } int main() { A* a = new C(); cout << "A* a = new C();" << endl; clock_t start, stop; const int count = 100000000; cout << "===== cast from pointer A to pointer C, should be not null =====" << endl; C* c = nullptr; start = clock(); for (int i = 0; i < count; i++) { c = dynamic_cast<C*>(a); } stop = clock(); cout << "dynamic_cast from A to C: " << c << ", " << "Time: " << stop - start << endl; c = nullptr; start = clock(); for (int i = 0; i < count; i++) { c = static_cast<C*>(a); } stop = clock(); cout << "static_cast from A to C: " << c << ", " << "Time: " << stop - start << endl; c = nullptr; start = clock(); for (int i = 0; i < count; i++) { c = fast_dynamic_cast<C>(a); } stop = clock(); cout << "fast_dynamic_cast from A to C: " << c << ", " << "Time: " << stop - start << endl; cout << "\n===== cast from pointer A to pointer B, should be not null =====" << endl; B* b = nullptr; start = clock(); for (int i = 0; i < count; i++) { b = dynamic_cast<B*>(a); } stop = clock(); cout << "dynamic_cast from A to B: " << b << ", " << "Time: " << stop - start << endl; b = nullptr; start = clock(); for (int i = 0; i < count; i++) { //b = static_cast<B*>(a); //compiler error } stop = clock(); cout << "static_cast from A to B: " << "compiler error" << endl; b = nullptr; start = clock(); for (int i = 0; i < count; i++) { b = fast_dynamic_cast<B>(a); } stop = clock(); cout << "fast_dynamic_cast from A to B: " << b << ", " << "Time: " << stop - start << endl; cout << "\n===== cast from pointer A to pointer D, should be null =====" << endl; D* d = nullptr; start = clock(); for (int i = 0; i < count; i++) { d = dynamic_cast<D*>(a); } stop = clock(); cout << "dynamic_cast from A to D: " << d << ", " << "Time: " << stop - start << endl; d = nullptr; start = clock(); for (int i = 0; i < count; i++) { d = static_cast<D*>(a); } stop = clock(); cout << "static_cast from A to D: " << d << ", " << "Time: " << stop - start << endl; d = nullptr; start = clock(); for (int i = 0; i < count; i++) { d = fast_dynamic_cast<D>(a); } stop = clock(); cout << "fast_dynamic_cast from A to D: " << d << ", " << "Time: " << stop - start << endl; delete a; a = nullptr; return 0; }