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