條款27 盡量少做轉型動作

2021-06-28 06:15:29 字數 3157 閱讀 1786

c++的設計目標之一是保證「型別錯誤」絕不會發生。但轉型(cast)破壞了型別系統(type system).

舊式轉型,c風格的轉型動作,如下:

(t)tmp     //將tmp轉型為t

t(tmp)     //同上,函式風格的轉型動作

c++提供四種新式轉型(new-style或c++-style casts)

1、const_cast(tmp)   通常用來將物件的常量性轉除,也是唯一有此能力的新式轉型操作符。

2、dynamic_cast(tmp)   主要用來執行「安全向下轉型」,決定某物件是否歸屬繼承體系中的某個型別。唯一無法由舊式語法執行的動作。唯一可能耗費大執行成本的動作。

3、reinterpret_cast(tmp)   低階轉型,實際可能取決於編譯器。很少用。

4、static_cast(tmp)  強迫隱式轉型(implict conversion),如int ->double,void*->type指標。但無法將const->nonconst,第一點已經提及。

新式比舊式轉型更受歡迎,主要有:1、**中更容易識別  2、轉型動作目標窄,編譯器越可能診斷出錯誤。如const->non const 除非使用const_cast.  否則無法通過

使用舊式轉型的例子,呼叫explict建構函式將乙個物件傳遞給個函式,如下:

class widget

;void fun(const widget& w);

fun(widget(8)); //舊式轉型,「函式風格「的轉型動作建立乙個widget

fun(static_cast(8)); //新式風格轉型動作建立乙個widget

任何乙個型別轉換(無論隱式或顯示),往往令編譯器編譯出執行期間的執行**,如下:

int   x,y;

double d=static_cast(x)/y;

x->y轉型肯定會產生一些**。因為int和double底層描述不同。

再如下:

class base{};

class derived: public base{};

derived d;

base* pb=&d;

基類指標指向派生類物件,有時上述兩指標值並不相同。這時會有偏移量加於

derived*

身上,以取得正確的

base*

值。以上表明單一物件可能擁有乙個以上的位址。(如base和derived指向它的位址)

考慮以下**,很多應用框架要求derived  class的虛函式的**第乙個動作是呼叫base class的對應函式,很容易寫出似是而非的**:

//錯誤做法

class wnd

; //基類size實現**

...};class cwnd: public wnd

};

雖然將「*this 」轉型為wnd,該**呼叫的並不是當前物件上的函式,而是轉型動作所建立的「

*this

物件的基類成分」的暫時副本上的函式

size()

最終結果是:未作用於當前物件,當前物件的base成分沒落實(如果wnd::size()修改了物件內容),僅僅變動了derive類部分。

//正確做法,去掉轉型操作

class cwnd: public wnd

};

dynamic_cast的實現版本執行效率相當慢,如乙個基於「class名稱字串的比較」的實現版本,如果在第四層單繼承體系內某個物件執行dynamic_cast,則可能會出現多大家四次strcpy呼叫。所以應在注重效率的**中對dynamic_cast保持慎重。

之所以用dynamic_cast,是在操作派生類物件時,而手頭卻只有指向其基類物件的指標或引用。so只能靠它來處理,主要有兩種方式避免這問題:

1、使用容器儲存指向派生類物件的指標(通常為智慧型指標),從而消除「基類指標處理派生類物件的需要」

//不應該這麼做

class wnd;

class cwnd: public wnd;

typedefstd::vector> vpw;

vpw wp;

...for(vpw::iterator it=wp.begin();it!=wp.end;++it)

if(cwnd*pcw=dynamic_cast(it->get())) //使用dymanic_cast轉型

pcw->fun1();

//而應該這麼做

typedef std::vector>vpcw;

vpcw cwp;

...for(vpcw::itertor it=cwp.begin();it!=wp.end;++it) //不使用dymanic_cast轉型

*it->fun();

這種做法的缺點是,無法用乙個容器指向多個派生類。如果要處理多種型別,就必須要多個容器。

2、方法二:基類介面處理各派生類,即基類提供虛函式,預設什麼都不做,如下**:

class wnd

; //預設什麼也不做

...};

class cwnd: public wnd

; //派生類的專屬實現

...};typedef std::vector>pw; //內含指標,指向所有可能的wnd型別

pw wp;

...for(pw::iterator it=wp.begin();it!=wp.end();++it) //沒有使用dynamic_cast

(*it)->fun();

以上兩種都是可行的dynamic_cast替代方案,如果有功效時我們就該使用。

絕對要避免的一件事是使用一連串的

dynamic_cast,這樣的**又大且慢,並基礎不穩。當繼承體系改變時要再次檢閱**。應用「基於虛函式的呼叫代替」。

需要記住的:

1、盡量避免轉型,在注重效率的**中避免dynamic_cast,如果需要轉型操作,試著以無需轉型的操作替代。

2、如果轉型必要,將其隱藏於某個函式。客戶可以呼叫,但無需將轉型放入自己**中。

3、寧可使用新式,避免使用舊式轉型。

條款27 盡量少做轉型動作

c 中的轉型語法包括舊式轉型和新式轉型。舊式轉換包括 t expression 將expression轉型為t,c風格的轉型動作 t expression 將expression轉型為t,函式風格的轉型動作 新式轉型包括 const cast expression const cast通常被用來將物...

條款27 盡量少做轉型動作

在談及顯式轉換之前,先簡單說說隱式轉換。int ival 0 ival 3.541 3 doubl向int轉換會丟失精度,編譯器會警告 從double到int轉換可能會丟失資料,結果ival等於6整數3被轉換為double型別,然後執行浮點型別加法操作,得double型別結果6.541,然後將dou...

條款27 盡量少做轉型動作

c 四種新式轉型 通常用來將物件的常量性轉除,將const轉成non const 用來執行 安全向下轉型 通過父類訪問子類 很少用到,忽略 用來強迫隱式轉換,例如將non const物件轉為const物件,或將int轉為double物件,也可以來執行上訴多種轉換的方向轉換,例如將void 指標轉為t...