盡量少做轉型動作 《Effective C 》

2021-08-13 20:57:56 字數 3657 閱讀 1797

背景:

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...