亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 學院 > 開發設計 > 正文

C++箴言:將強制轉型減到最少

2019-11-17 05:06:28
字體:
來源:轉載
供稿:網友
C++ 的規則設計為保證不會發生類型錯誤。在理論上,假如你的程序想順利地通過編譯,你就不應該試圖對任何對象做任何不安全的或無意義的操作。這是一個非常有價值的保證,你不應該輕易地放棄它。

  不幸的是,強制轉型破壞了類型系統。它會引起各種各樣的麻煩,其中一些輕易被察覺,另一些則格外地微妙。假如你從 C,java,或 C# 轉到 C++,請一定注重,因為強制轉型在那些語言中比在 C++ 中更有必要,危險也更少。但是 C++ 不是 C,也不是 Java,也不是 C#。在這一語言中,強制轉型是一個你必須聚精會神才可以靠近的特性。

  我們就從回顧強制轉型的語法開始,因為對于同樣的強制轉型通常有三種不同的寫法。C 風格(C-style)強制轉型如下:

  

  (T) eXPRession // cast expression to be of type T

  函數風格(Function-style)強制轉型使用這樣的語法:

  

  T(expression) // cast expression to be of type T

  這兩種形式之間沒有本質上的不同,它純粹就是一個把括號放在哪的問題。我把這兩種形式稱為舊風格(old-style)的強制轉型。

  C++ 同時提供了四種新的強制轉型形式(通常稱為新風格的或 C++ 風格的強制轉型):

  

  const_cast(expression)

  dynamic_cast(expression)

  reinterpret_cast(expression)

  static_cast(expression)

  每一種適用于特定的目的:

  ·const_cast 一般用于強制消除對象的常量性。它是唯一能做到這一點的 C++ 風格的強制轉型。

  ·dynamic_cast 主要用于執行“安全的向下轉型(safe downcasting)”,也就是說,要確定一個對象是否是一個繼續體系中的一個特定類型。它是唯一不能用舊風格語法執行的強制轉型。也是唯一可能有重大運行時代價的強制轉型。(過一會兒我再提供細節。)

  ·reinterpret_cast 是特意用于底層的強制轉型,導致實現依靠(implementation-dependent)(就是說,不可移植)的結果,例如,將一個指針轉型為一個整數。這樣的強制轉型在底層代碼以外應該極為罕見。在本書中我只用了一次,而且還僅僅是在討論你應該如何為裸內存(raw memory)寫一個調諧分配者(debugging allocator)的時候。

  ·static_cast 可以被用于強制隱型轉換(例如,non-const 對象轉型為 const 對象(就像 Item 3 中的),int 轉型為 double,等等)。它還可以用于很多這樣的轉換的反向轉換(例如,void* 指針轉型為有類型指針,基類指針轉型為派生類指針),但是它不能將一個 const 對象轉型為 non-const 對象。(只有 const_cast 能做到。)

  舊風格的強制轉型依然合法,但是新的形式更可取。首先,在代碼中它們更輕易識別(無論是人還是像 grep 這樣的工具都是如此),這樣就簡化了在代碼中尋找類型系統被破壞的地方的過程。第二,更精確地指定每一個強制轉型的目的,使得編譯器診斷使用錯誤成為可能。例如,假如你試圖使用一個 const_cast 以外的新風格強制轉型來消除常量性,你的代碼將無法編譯。

  當我要調用一個 explicit 構造函數用來傳遞一個對象給一個函數的時候,大概就是我僅有的使用舊風格的強制轉換的時候。例如:

  

  class Widget {

  public:

  explicit Widget(int size);

  ...

  };

  

  void doSomeWork(const Widget& w);

  

  doSomeWork(Widget(15)); // create Widget from int

  // with function-style cast

  

  doSomeWork(static_cast(15)); // create Widget from int

  // with C++-style cast

  

更多文章 更多內容請看C/C++技術專題專題,或 由于某種原因,有條不紊的對象創建感覺上不像一個強制轉型,所以在這個強制轉型中我多半會用函數風格的強制轉型代替 static_cast。反過來說,在你寫出那些導致核心崩潰(core dump)的代碼時,你通常都感覺你有恰當的原因,所以你最好忽略你的感覺并始終都使用新風格的強制轉型。


  很多程序員認為強制轉型除了告訴編譯器將一種類型看作另一種之外什么都沒做,但這是錯誤的。任何種類的類型轉換(無論是通過強制轉型的顯式的還是編譯器添加的隱式的)都會導致運行時的可執行代碼。例如,在這個代碼片斷中,

  

  int x, y;

  ...

  double d = static_cast(x)/y; // divide x by y, but use

  // floating point division

  int x 到 double 的強制轉型理所當然要生成代碼,因為在大多數系統架構中,一個 int 的底層表示與 double 的不同。這可能還不怎么令人吃驚,但是下面這個例子可能會讓你稍微開一下眼:

  

  class Base { ... };

  

  class Derived: public Base { ... };

  

  Derived d;

  

  Base *pb = &d; // implicitly convert Derived* → Base*

  這里我們只是創建了一個指向派生類對象的基類指針,但是有時候,這兩個指針的值并不相同。在當前情況下,會在運行時在 Derived* 指針上應用一個偏移量以得到正確的 Base* 指針值。

  這后一個例子表明一個單一的對象(例如,一個類型為 Derived 的對象)可能會有不止一個地址(例如,它的被一個 Base* 指針指向的地址和它的被一個 Derived* 指針指向的地址)。這在 C 中就不會發生,也不會在 Java 中發生,也不會在 C# 中發生,它僅在 C++ 中發生。實際上,假如使用了多繼續,則一定會發生,但是在單繼續下也會發生。與其它事情合在一起,就意味著你應該總是避免對 C++ 如何擺放事物做出假設,你當然也不應該基于這樣的假設執行強制轉型。例如,將一個對象的地址強制轉型為 char* 指針,然后對其使用指針運算,這幾乎總是會導致未定義行為。

  但是請注重我說一個偏移量是“有時”被需要。對象擺放的方法和他們的地址的計算方法在不同的編譯器之間有所變化。這就意味著僅僅因為你的“我知道事物是如何擺放的”而使得強制轉型能工作在一個平臺上,并不意味著它們也能在其它平臺工作。這個世界被通過痛苦的道路學得這條經驗的可憐的程序員所布滿。 關于強制轉型的一件有趣的事是很輕易寫出看起來對(在其它語言中也許是對的)實際上錯的東西。例如,許多應用框架(application framework)要求在派生類中實現虛成員函數時要首先調用它們的基類對應物。假設我們有一個 Window 基類和一個 SpecialWindow 派生類,它們都定義了虛函數 onResize。進一步假設 SpecialWindow 的 onResize 被期望首先調用 Window 的 onResize。這就是實現這個的一種方法,它看起來正確實際并不正確:

  

  class Window { // base class

  public:

  virtual void onResize() { ... } // base onResize impl

  ...

  };

  

  class SpecialWindow: public Window { // derived class

  public:

  virtual void onResize() { // derived onResize impl;

  static_cast(*this).onResize(); // cast *this to Window,

  // then call its onResize;

  // this doesn’t work!

  

  ... // do SpecialWindow-

  } // specific stuff

  

  ...

  

  };

  我突出了代碼中的強制轉型。(這是一個新風格的強制轉型,但是使用舊風格的強制轉型也于事無補。)正像你所期望的,代碼將 *this 強制轉型為一個 Window。因此調用 onResize 的結果就是調用 Window::onResize。你也許并不期待它沒有調用當前對象的那個函數!作為替代,強制轉型創建了一個 *this 的基類部分的新的,臨時的拷貝,然后調用這個拷貝的 onResize!上面的代碼沒有調用當前對象的 Window::onResize,然后再對這個對象執行 SpecialWindow 特有的動作——它在對當前對象執行 SpecialWindow 特有的動作之前,調用了當前對象的基類部分的一份拷貝的 Window::onResize。假如 Window::onResize 改變了當前對象(可能性并不小,因為 onResize 是一個 non-const 成員函數),當前對象并不會改變。作為替代,那個對象的一份拷貝被改變。假如 SpecialWindow::onResize 改變了當前對象,無論如何,當前對象將被改變,導致的境況是那些代碼使當前對象進入一種病態,沒有做基類的變更,卻做了派生類的變更。

  解決方法就是消除強制轉型,用你真正想表達的來代替它。你不應該哄騙編譯器將 *this 當作一個基類對象來處理,你應該調用當前對象的 onResize 的基類版本。就是這樣:

  

  class SpecialWindow: public Window {


  public:

  virtual void onResize() {

  Window::onResize(); // call Window::onResize

  ... // on *this

  }

  ...

  

  };

  這個例子也表明假如你發現自己要做強制轉型,這就是你可能做錯了某事的一個信號。在你想用 dynamic_cast 時尤其如此。

  在探究 dynamic_cast 的設計意圖之前,值得留意的是很多 dynamic_cast 的實現都相當慢。例如,至少有一種通用的實現部分地基于對類名字進行字符串比較。假如你在一個位于四層深的單繼續體系中的對象上執行 dynamic_cast,在這樣一個實現下的每一個 dynamic_cast 都要付出相當于四次調用 strcmp 來比較類名字的成本。對于一個更深的或使用了多繼續的繼續體系,付出的代價會更加昂貴。一些實現用這種方法工作是有原因的(它們不得不這樣做以支持動態鏈接)。盡管如此,除了在普遍意義上警惕強制轉型外,在性能敏感的代碼中,你應該非凡警惕 dynamic_casts。

  對 dynamic_cast 的需要通常發生在這種情況下:你要在一個你確信為派生類的對象上執行派生類的操作,但是你只能通過一個基類的指針或引用來操控這個對象。有兩個一般的方法可以避免這個問題。


更多文章 更多內容請看C/C++技術專題專題,或 第一個,使用存儲著直接指向派生類對象的指針的容器,從而消除通過基類接口操控這個對象的需要。例如,假如在我們的 Window/SpecialWindow 繼續體系中,只有 SpecialWindows 支持 blinking,對于這樣的做法:

  

  class Window { ... };

  

  class SpecialWindow: public Window {

  public:

  void blink();

  ...

  };

  typedef // see Item 13 for info

  std::vector > VPW; // on tr1::shared_ptr

  

  VPW winPtrs;

  

  ...

  

  for (VPW::iterator iter = winPtrs.begin(); // undesirable code:

  iter != winPtrs.end(); // uses dynamic_cast

  ++iter) {

  if (SpecialWindow *psw = dynamic_cast(iter->get()))

  psw->blink();

  }

  設法用如下方法代替:

  

  typedef std::vector > VPSW;

  

  VPSW winPtrs;

  

  ...

  

  for (VPSW::iterator iter = winPtrs.begin(); // better code: uses

  iter != winPtrs.end(); // no dynamic_cast

  ++iter)

  (*iter)->blink();

  當然,這個方法不答應你在同一個容器中存儲所有可能的 Window 的派生類的指針。為了與不同的窗口類型一起工作,你可能需要多個類型安全(type-safe)的容器。

  一個候選方法可以讓你通過一個基類的接口操控所有可能的 Window 派生類,就是在基類中提供一個讓你做你想做的事情的虛函數。例如,盡管只有 SpecialWindows 能 blink,在基類中聲明這個函數,并提供一個什么都不做的缺省實現或許是有意義的:

  

  class Window {

  public:

  virtual void blink() {} // default impl is no-op;

  ... // see Item 34 for why

  }; // a default impl may be

  // a bad idea

  

  class SpecialWindow: public Window {

  public:

  virtual void blink() { ... }; // in this class, blink

  ... // does something


  };

  

  typedef std::vector > VPW;

  

  VPW winPtrs; // container holds

  // (ptrs to) all possible

  ... // Window types

  

  for (VPW::iterator iter = winPtrs.begin();

  iter != winPtrs.end();

  ++iter) // note lack of

  (*iter)->blink(); // dynamic_cast

  無論哪種方法——使用類型安全的容器或在繼續體系中上移虛函數——都不是到處適用的,但在很多情況下,它們提供了 dynamic_casting 之外另一個可行的候選方法。當它們可用時,你應該加以利用。

  你應該絕對避免的一件東西就是包含了極聯 dynamic_casts 的設計,也就是說,看起來類似這樣的任何東西:

  

  class Window { ... };

  

  ... // derived classes are defined here

  

  typedef std::vector > VPW;

  

  VPW winPtrs;

  

  ...

  

  for (VPW::iterator iter = winPtrs.begin(); iter != winPtrs.end(); ++iter)

  {

   if (SpecialWindow1 *psw1 = dynamic_cast(iter->get())) { ... }

  

   else if (SpecialWindow2 *psw2 = dynamic_cast(iter->get())) { ... }

  

   else if (SpecialWindow3 *psw3 = dynamic_cast(iter->get())) { ... }

  

  ...

  }

  這樣的 C++ 會生成的代碼又大又慢,而且很脆弱,因為每次 Window 類繼續體系發生變化,所有這樣的代碼都要必須被檢查,以確認是否需要更新。(例如,假如增加了一個新的派生類,在上面的極聯中或許就需要加入一個新的條件分支。)看起來類似這樣的代碼應該總是用基于虛函數的調用的某種東西來替換。 好的 C++ 極少使用強制轉型,但在通常情況下完全去除也不實際。例如,從 int 到 double 的強制轉型,就是對強制轉型的合理運用,雖然它并不是絕對必要。(那些代碼應該被重寫,聲明一個新的類型為 double 的變量,并用 x 的值進行初始化。)就像大多數可疑的結構成分,強制轉型應該被盡可能地隔離,典型情況是隱藏在函數內部,用函數的接口保護調用者遠離內部的污穢的工作。

  Things to Remember

  ·避免強制轉型的隨時應用,非凡是在性能敏感的代碼中應用 dynamic_casts,假如一個設計需要強制轉型,設法開發一個沒有強制轉型的侯選方案。

  ·假如必須要強制轉型,設法將它隱藏在一個函數中??蛻艨梢杂谜{用那個函數來代替在他們自己的代碼中加入強制轉型。

  ·盡量用 C++ 風格的強制轉型替換舊風格的強制轉型。它們更輕易被注重到,而且他們做的事情也更加明確。

  

更多文章 更多內容請看C/C++技術專題專題,或

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
欧美黑人狂野猛交老妇| 欧美精品18videos性欧美| 亚洲已满18点击进入在线看片| 亚洲一区二区福利| 5278欧美一区二区三区| 国产欧美精品日韩| 亚洲综合中文字幕68页| 亚洲精品国产免费| 亚洲大胆人体视频| 中文字幕日韩高清| 久久人人爽亚洲精品天堂| 欧美日韩人人澡狠狠躁视频| 91精品在线观| 亚洲天堂男人的天堂| 国产亚洲欧洲高清| 国产a级全部精品| 久久亚洲电影天堂| 亚洲视频视频在线| 欧美精品18videosex性欧美| 成人a免费视频| 亚洲成人网久久久| 日韩视频在线免费| 欧美日韩色婷婷| 欧美色道久久88综合亚洲精品| 欧美不卡视频一区发布| 中文字幕一区二区三区电影| 91精品国产91久久| 亚洲男人第一网站| 97色伦亚洲国产| 日韩一区二区三区国产| 国产精品96久久久久久又黄又硬| 亚洲精品网址在线观看| 久久久久一本一区二区青青蜜月| 久久久精品国产网站| 国产精品ⅴa在线观看h| 精品久久久国产精品999| 日韩视频免费中文字幕| 国产精品一区二区久久久久| 精品国产乱码久久久久酒店| 91国内免费在线视频| 91精品视频播放| 日韩中文字幕不卡视频| 国产欧美一区二区三区四区| 亚洲综合精品伊人久久| 日韩中文字幕在线观看| 久久精品成人动漫| 欧美激情欧美激情在线五月| 久久久久久久久中文字幕| 亚洲欧美日韩一区二区三区在线| 精品美女久久久久久免费| 97碰在线观看| 成人美女av在线直播| 日本一欧美一欧美一亚洲视频| 日韩精品视频免费专区在线播放| 亚洲精品www| 成人淫片在线看| 国产日韩视频在线观看| 91精品久久久久久久久| 68精品久久久久久欧美| 91精品久久久久久久久久久久久| 亚洲欧美一区二区三区情侣bbw| 亚洲精品影视在线观看| 少妇高潮久久久久久潘金莲| 国产成人精品一区二区在线| 亚洲精品在线视频| 激情av一区二区| 91精品在线播放| 国产欧美一区二区| 欧美日韩国产综合视频在线观看中文| 国产欧美日韩亚洲精品| 久久久久久久久国产精品| 亚洲成色777777女色窝| 成人国产精品色哟哟| 国产欧美精品在线| 欧美精品18videos性欧美| 国产精品流白浆视频| 91免费人成网站在线观看18| 亚洲男人天堂手机在线| 性色av一区二区三区免费| 国产精品av免费在线观看| 色中色综合影院手机版在线观看| 精品国产精品三级精品av网址| 中文字幕无线精品亚洲乱码一区| 亚洲石原莉奈一区二区在线观看| 欧美成人网在线| 亚洲性日韩精品一区二区| 久久精品国产91精品亚洲| 色悠久久久久综合先锋影音下载| 91精品国产自产91精品| 最新国产精品亚洲| 亚洲国产精彩中文乱码av在线播放| 久久九九有精品国产23| 国产69精品久久久久9999| 91久久精品国产91久久| 国产精品久久一区主播| 亚洲一区二区三区xxx视频| 国产91精品视频在线观看| 亚洲成人在线视频播放| 色偷偷av一区二区三区| 国产成一区二区| 亚洲欧美日韩另类| 大胆欧美人体视频| 97精品国产97久久久久久春色| 亚洲国产成人精品一区二区| 国产精品精品一区二区三区午夜版| 成人激情免费在线| 九九热这里只有在线精品视| 日韩欧美在线观看视频| 欧美国产精品日韩| 欧美一级大片在线免费观看| 欧美一区亚洲一区| 最近2019中文字幕在线高清| 亚洲精品久久久久久久久久久久| 91在线精品播放| 日韩av在线不卡| 欧美另类99xxxxx| 亚洲精品一区二区在线| 欧美国产激情18| 欧美亚洲第一区| 久久精品一区中文字幕| 亚洲欧美日韩高清| 在线观看视频亚洲| 亚洲www永久成人夜色| 亚洲国产精品人人爽夜夜爽| 欧美成人合集magnet| 国产精品99久久久久久www| 国产玖玖精品视频| 国产精品日本精品| 九九热这里只有在线精品视| 欧美大胆a视频| 欧美老少配视频| 亚洲电影av在线| 欧美成在线视频| 疯狂欧美牲乱大交777| 亚洲成人久久一区| 91久久中文字幕| 国产极品jizzhd欧美| 久久99精品久久久久久噜噜| 国产婷婷色综合av蜜臀av| 亚洲精品久久久久久久久久久| 茄子视频成人在线| 亚洲xxx视频| 欧美重口另类videos人妖| 2019中文字幕在线免费观看| 日本久久久久久久久| 国产精品久久久久aaaa九色| 97久久伊人激情网| 一区二区三区精品99久久| 亚洲高清av在线| 亚洲国产成人av在线| 国产欧美va欧美va香蕉在| 亚洲欧洲日韩国产| 成人黄色大片在线免费观看| 欧美在线观看日本一区| 欧美丰满老妇厨房牲生活| 成人性生交大片免费看视频直播| 精品久久久久久久久久| 2018日韩中文字幕| 国语自产精品视频在线看一大j8| 亚洲的天堂在线中文字幕| 丝袜亚洲另类欧美重口| 国产精品久久9| 亚洲国产精品va在线看黑人| 亚洲欧美一区二区三区四区|