背景:
c++規則的設計目標之一:保證型別錯誤絕不可發生。(非常重要!)
乙個程式能乾淨地通過編譯,表示:該程式不會在任何物件身上執行 任何不安全,無意義、愚蠢的操作。
但轉型破壞型別系統。可能會導致麻煩。
c++提供的轉型方案:
const_cast
(expression)
dynamic_cast
(expression)
reinterpret_cast
(expression)
static_cast
(expression)
各自分工不同:
const_cast
(expression):將物件的常量性轉除。意思就是將乙個物件型別是const的,經過該轉換之後,就不是const型別的。
dynamic_cast
(expression):用於執行安全向下型別轉換。意味:決定某物件是否歸屬繼承體系中的某個型別。也是唯一可能消耗重大執行成本的轉型動作。
reinterpret_cast
(expression):執行低階轉型。實際轉型動作以及結果可能取決於編譯器。這表示它不可移植。比如將指向int型的指標,轉型為int型別。很少見~~~
static_cast
(expression):強迫隱式轉換。比如:將非const型別轉型為const,將int轉為double型別等。但無法將const型別轉型為非const型別,只有const_cast可以實現。
為何少用或不用轉型?
1. 隱式轉型或顯示轉型,有時候需要乙個偏移量來獲取到轉型後的物件。
示例:class base;
class derived:public base;
derived d;
base* pb = &d;//隱式將derived*轉換為base*
或base* pb = static_castd;//顯示型別轉換
此處目的:將乙個基類指標指向乙個派生類物件。
編譯器實際執行的動作:
這種情況下會有個偏移量在執行期被施行於derived* 指標身上,用於取得正確的base*型別指標值。
意思就是:
base* pb指向的位址是 derived* d指標的位址經過乙個offset偏移量計算得到。此時的&d的位址代表的是base 在derived類中的偏移位置。
但是&d本身有個位址,是指向自身derived的。
這也就出現乙個指標,有可能有多個不同的位址。這就是c++。 其他語言是不可能發生這種情況的。
因此:
在這種情況下,不要將物件位址轉型為其他型別的指標,比如char*,然後進行指標算術,會導致無定義行為。
示例:目的:b呼叫method方法之前,必須呼叫a方法的method。
class a; // 類a的實現方法
};
class b: public a
};
案例解析:
這段程式將*this轉型為a,此時得到的a是*this物件的基類成分,是通過偏移量計算得到的a 。此時a是乙個副本,通過副本呼叫method方法,最後執行b的專屬動作。
此時如果a::method修改了物件內容(method不是const成員函式,可更改),當前物件其實並沒有改變,改變的只是副本。
然而b::method修改 了物件,當前物件就會被改變。這就造成:基類成分的更改沒有落實,派生類成分的更改倒是落實更改。
這就是通過轉型引入的問題。
解決方案:
告訴編譯器,你的真實的目的。不要轉型。
如下:class b: public a
};
3.dynamic_cast 的使用,更需謹慎
dynamic_cast 背景:
dynamic_cast的實現版本有很多,各自實現版本執行速度相當慢。
舉例:
有個普遍的實現版本是基於class類名稱的字串進行比較找到轉型後的物件。 假如在乙個四層單繼承體系中,對某個物件身上執行dynamic_cast,
則每一次dynamic_cast可能會耗用多達四次的strcmp呼叫,用於比較名稱。深度繼承或多重繼承的成本更高!!!
因此對於效率要求較高的**中,使用dynamic_cast需要提高警惕。
替代使用dynamic_cast轉型的場景?
背景:
想使用dynamic_cast轉型,通常是想在派生類身上執行派生類成員函式,但當前環境中只有乙個指向基類的指標或引用,只能靠dynamic_cast轉型實現。
解決方案:
1. 使用容器並在其中儲存直接指向派生類物件的指標(通常是智慧型指標)
舉例:class a;
class b: public a
該方案就是直接使用dynamic_cast實現訪問派生類的成員函式方法。但是這個弊端,上面已述:採用普遍的比較類名稱來找到目標物件,耗費的成本太高,效率不好。
typedef std::vector< std::shared_ptr> vpb;
vpb vptrs;
for(vpa::itertor iter = vptrs.begin();iter != vptrs.end();++iter);//什麼也不實現。 };
class b:public a;
//該類實現具體業務
};typedef std::vector> vpa;
//包含指向所有可能的a型別。有可能是b,有可能是c,d,e,f等
vpa vptrs;
...for(vpa::iterator iter = vptrs.begin();iter != vptrs.end();++iter)
總結:
1.如果可以,盡量避免轉型。特別是在注重效率的**中避免dynamic_cast操作。如果有設計需要轉型動作,
嘗試無需轉型的設計方案:
a:使用容器並在其中儲存直接指向派生類物件的指標(通常是智慧型指標)
b:在基類提供virtual函式,乙份什麼也不實現的**。
2.隱式轉型或顯示轉型,有時候需要乙個偏移量來獲取到轉型後的物件
3.不要欺騙編譯器,請真實表達自己想做的事情
27 盡量少做轉型動作
1 c 是強型別語言,保證型別錯誤不會發生,轉型會破壞型別系統。c語言提供了強制轉型語法t expn 或者t expn c 提供了新式轉型。const cast expn static cast expn dynamic cast expn reinterpret cast expn 強烈建議使用新...
條款27 盡量少做轉型動作
c 中的轉型語法包括舊式轉型和新式轉型。舊式轉換包括 t expression 將expression轉型為t,c風格的轉型動作 t expression 將expression轉型為t,函式風格的轉型動作 新式轉型包括 const cast expression const cast通常被用來將物...
條款27 盡量少做轉型動作
c 的設計目標之一是保證 型別錯誤 絕不會發生。但轉型 cast 破壞了型別系統 type system 舊式轉型,c風格的轉型動作,如下 t tmp 將tmp轉型為t t tmp 同上,函式風格的轉型動作 c 提供四種新式轉型 new style或c style casts 1 const cas...