青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

洛譯小筑

別來(lái)無(wú)恙,我的老友…
隨筆 - 45, 文章 - 0, 評(píng)論 - 172, 引用 - 0
數(shù)據(jù)加載中……

[ECPP讀書(shū)筆記 條目9] 永遠(yuǎn)不要在構(gòu)造或析構(gòu)的過(guò)程中調(diào)用虛函數(shù)

讓我們直切正題:在程序進(jìn)行構(gòu)造或析構(gòu)期間,你絕不能調(diào)用虛函數(shù),這是因?yàn)檫@樣的調(diào)用并不會(huì)按你所期望的執(zhí)行,即使能夠順利執(zhí)行,你也不會(huì)覺(jué)得十分舒服。如果你曾經(jīng)是一個(gè)Java或C#的程序員,并且在最近期望返回C++的懷抱,那么請(qǐng)你格外留意本條目,因?yàn)樵谶@一問(wèn)題上,C++與其他語(yǔ)言走的是完全不同的兩條路線。

假設(shè)有一個(gè)股票交易模擬系統(tǒng),你為它編寫(xiě)了一個(gè)類的層次化結(jié)構(gòu),其中包括實(shí)現(xiàn)購(gòu)買(mǎi)、拋售等功能的類。這類交易應(yīng)該是可以審計(jì)的,這一點(diǎn)很重要,所以說(shuō)每創(chuàng)建一次交易時(shí),都應(yīng)該在日志中創(chuàng)建一條審計(jì)相關(guān)內(nèi)容的記錄。下面是一個(gè)看似合理的解決方案:

class Transaction {                      // 所有交易的基類

public:

  Transaction();

  virtual void logTransaction() const = 0; // 作類型相關(guān)的記錄

  ...

};

 

Transaction::Transaction()               // 基類構(gòu)造函數(shù)的實(shí)現(xiàn)

{

  ...

  logTransaction();                       // 最后,記錄這次交易

}

 

class BuyTransaction: public Transaction { // 派生類

public:

  virtual void logTransaction() const;   // 當(dāng)前類型交易是如何記錄的

  ...

};

 

class SellTransaction: public Transaction {    // 派生類

public:

  virtual void logTransaction() const;   // 當(dāng)前類型交易是如何記錄的

  ...

};

請(qǐng)考慮一下在下邊的代碼運(yùn)行時(shí)會(huì)發(fā)生什么:

BuyTransaction b;

很明顯的是此時(shí)BuyTransaction的構(gòu)造函數(shù)將被調(diào)用,但是,首先必須調(diào)用Transaction的構(gòu)造函數(shù)。對(duì)于一個(gè)派生的對(duì)象,其基類那一部分會(huì)首先得到構(gòu)造,然后才是派生類的部分。Transaction的構(gòu)造函數(shù)中最后一行調(diào)用了虛函數(shù)logTransaction,意外的事情就從這里發(fā)生了:此處調(diào)用的是Translation版本的logTransaction函數(shù),而不是BuyTransaction版本的——即使此處創(chuàng)建的對(duì)象是BuyTransaction類型的。在基類部分的構(gòu)造過(guò)程中,虛函數(shù)永遠(yuǎn)也不會(huì)嘗試去匹配派生類部分。取而代之的是,對(duì)象仍然保持基類的行為。更隨意一點(diǎn)的說(shuō)法是,在基類部分構(gòu)造的過(guò)程中,虛函數(shù)并不會(huì)被構(gòu)造。

這一行為看上去匪夷所思,但是這里有很充足的理由來(lái)解釋它。由于基類的構(gòu)造函數(shù)先于派生類運(yùn)行,在基類構(gòu)造函數(shù)運(yùn)行的時(shí)候,派生類的數(shù)據(jù)成員還沒(méi)有被初始化。如果在基類構(gòu)造函數(shù)向下匹配派生類時(shí)調(diào)用了虛函數(shù),那么基類的函數(shù)幾乎一定會(huì)調(diào)用局部數(shù)據(jù)成員,但此時(shí)這些數(shù)據(jù)成員此時(shí)尚未得到初始化。你的程序?qū)?huì)出現(xiàn)無(wú)盡的未定義行為,你也會(huì)在整夜受到瑣碎的調(diào)試工作的折磨。當(dāng)一個(gè)對(duì)象中某些部分尚未初始化的時(shí)候,此時(shí)對(duì)其進(jìn)行調(diào)用會(huì)存在內(nèi)在的危險(xiǎn),所以C++不允許你這樣做。

實(shí)際情況比上文介紹的更為基礎(chǔ)。對(duì)于一個(gè)派生類的對(duì)象來(lái)說(shuō),在其進(jìn)行基類部分構(gòu)造工作的時(shí)候,這一對(duì)象的類型就是基類的。不僅僅虛函數(shù)會(huì)解析為基類的,而且C++中“使用運(yùn)行時(shí)類型信息”的部分(比如dynamic_cast(參見(jiàn)條目27)和typeid)也會(huì)將其看作基類類型的對(duì)象。在我們的示例中,當(dāng)調(diào)用Transaction的構(gòu)造函數(shù)以初始化一個(gè)BuyTransaction對(duì)象的基類部分時(shí),這一對(duì)象是Transaction類型的。C++的任何一部分都會(huì)這樣處理,這種處理方式是有意義的:由于這個(gè)對(duì)象的BuyTransaction部分尚未得到初始化,所以假定它們不存在才是最安全的處理方法。對(duì)于一個(gè)派生類對(duì)象來(lái)說(shuō),只有派生類的構(gòu)造函數(shù)開(kāi)始執(zhí)行,這個(gè)對(duì)象才會(huì)變成該派生類的對(duì)象。

對(duì)于析構(gòu)過(guò)程可以應(yīng)用同樣的推理方式。一旦派生類的析構(gòu)函數(shù)運(yùn)行完畢,對(duì)象中派生類的那一部分?jǐn)?shù)據(jù)成員將取得未定義的值,所以C++會(huì)認(rèn)為它們不再存在。在進(jìn)入基類的析構(gòu)函數(shù)時(shí),這個(gè)對(duì)象將成為一個(gè)基類對(duì)象,C++的所有部分——包括虛函數(shù)、dynamic_cast等等——都會(huì)這樣對(duì)待該對(duì)象。

在上文的示例代碼中,Transaction的構(gòu)造函數(shù)對(duì)一個(gè)虛函數(shù)進(jìn)行了一次直接調(diào)用,很顯然這樣做是違背本條中的指導(dǎo)方針的。這樣的違規(guī)實(shí)在太容易發(fā)現(xiàn)了,一些編譯器都會(huì)對(duì)其做出警告。(其他一些則不會(huì)。參見(jiàn)條目53對(duì)編譯器警告信息的討論)即使沒(méi)有警告,問(wèn)題也一定會(huì)在運(yùn)行之前變得很明顯,這是因?yàn)?span style="font-family:"Courier New";">Transaction中的logTransaction函數(shù)是純虛函數(shù),除非它得到了定義(不像是真的,但存在這種可能,參見(jiàn)條目34),程序才有可能會(huì)得到連接,其他情況都會(huì)報(bào)錯(cuò):連接器無(wú)法找到必要的Transaction::logTransaction的具體實(shí)現(xiàn)。

查找構(gòu)造或析構(gòu)過(guò)程中對(duì)虛函數(shù)的調(diào)用并不總是一帆風(fēng)順的。如果Transaction擁有多個(gè)構(gòu)造函數(shù),它們所進(jìn)行的工作中有一部分是相同的,那么可以將這些公共的初始化代碼(包括對(duì)logTransaction的調(diào)用)放入一個(gè)私有的非虛擬的初始化函數(shù)中,這樣做可以避免代碼重復(fù),從軟件工程角度來(lái)講這似乎是一個(gè)很好的做法,我們將這一函數(shù)命名為init

class Transaction {

public:

  Transaction()

  { init(); }                      // 調(diào)用非虛函數(shù)...

 

  virtual void logTransaction() const = 0;

  ...

 

private:

  void init()

  {

    ...

    logTransaction();              // ...而它卻調(diào)用一個(gè)虛函數(shù)!

  }

};

這樣的代碼與前文中的版本使用的是同一理念,但是這樣做所帶來(lái)的危害更為隱蔽和嚴(yán)重,這是因?yàn)檫@樣的代碼會(huì)得到正常的編譯和連接而不會(huì)報(bào)錯(cuò)。這種情況下,由于logTransactionTransaction中的一個(gè)純虛函數(shù),大多數(shù)運(yùn)行時(shí)系統(tǒng)將會(huì)在調(diào)用這個(gè)純虛函數(shù)時(shí)中止程序(通常情況下會(huì)針對(duì)這一結(jié)果顯示出一個(gè)消息)。然而如果logTransaction是一個(gè)“正常的”虛函數(shù)(也就是說(shuō),不是純虛的),并且在Transaction中給出了一些實(shí)現(xiàn),那么此時(shí)將調(diào)用這一版本的logTranscation,程序?qū)?huì)“愉快地一路小跑”下去,至于為什么在創(chuàng)建派生類對(duì)象時(shí)會(huì)調(diào)用錯(cuò)誤的logTransaction版本,程序可就不管這一套了。避免這類問(wèn)題的唯一途徑就是:在正在創(chuàng)建或銷毀的對(duì)象的構(gòu)造函數(shù)和析構(gòu)函數(shù)中,確保永遠(yuǎn)不要調(diào)用虛函數(shù),對(duì)于構(gòu)造函數(shù)和析構(gòu)函數(shù)所調(diào)用的所有函數(shù)都應(yīng)遵守這一約定。

那么,每當(dāng)創(chuàng)建一個(gè)Transaction層次結(jié)構(gòu)中的對(duì)象時(shí),如何確保去調(diào)用正確的logTransaction版本呢?顯然地,在Transaction的構(gòu)造函數(shù)中調(diào)用一個(gè)虛函數(shù)是一個(gè)錯(cuò)誤的做法。

為解決這一問(wèn)題我們可以另辟蹊徑。方案之一就是:將Transaction中的logTransaction變?yōu)橐粋€(gè)非虛函數(shù),然后要求派生類的構(gòu)造函數(shù)把必要的日志記錄傳遞給Transaction的構(gòu)造函數(shù)。這個(gè)構(gòu)造函數(shù)對(duì)于非虛logTransaction的調(diào)用就是安全的。就像這樣:

class Transaction {

public:

  explicit Transaction(const std::string& logInfo);

 

  void logTransaction(const std::string& logInfo) const;

  // 現(xiàn)在logTransaction是非虛函數(shù)

  ...

};

 

Transaction::Transaction(const std::string& logInfo)

{

  ...

  logTransaction(logInfo);         // 現(xiàn)在調(diào)用的是一個(gè)非虛函數(shù)

}

 

class BuyTransaction: public Transaction {

public:

 BuyTransaction( parameters )

 : Transaction(createLogString( parameters ))

  { ... }                          // 將記錄傳遞給基類構(gòu)造函數(shù)

 

   ...

 

private:

  static std::string createLogString( parameters );

};

換句話說(shuō),你不能使用虛函數(shù)在基類構(gòu)造過(guò)程中向下調(diào)用派生類的部分,作為一種補(bǔ)償,你可以讓派生類將一些必要的構(gòu)造信息向上傳遞給基類的構(gòu)造函數(shù)。

請(qǐng)注意上述示例里BuyTransaction類中(私有的)靜態(tài)函數(shù)createLogString的使用。這里使用了一個(gè)輔助函數(shù)創(chuàng)建一個(gè)值來(lái)傳遞給基類的構(gòu)造函數(shù),通常情況下這樣做更為方便(而且更具備可讀性),這樣做使為基類提供所需信息的成員初始化表變得更加直觀。這是因?yàn)檫@樣做解決了“為基類提供所需信息的成員初始化表”不直觀的問(wèn)題。意外調(diào)用初生的BuyTransaction對(duì)象中那些尚未初始化的數(shù)據(jù)成員是十分危險(xiǎn)的,由于createLogString是靜態(tài)的,此處便不存在這一危險(xiǎn)。這一點(diǎn)很重要,因?yàn)檫@些數(shù)據(jù)成員正處于未定義的狀態(tài),這一事實(shí)便解釋了為什么“在基類部分構(gòu)造或析構(gòu)期間調(diào)用虛函數(shù),不會(huì)在第一時(shí)間向下匹配派生類”。

時(shí)刻牢記

不要在構(gòu)造和析構(gòu)的過(guò)程中調(diào)用虛函數(shù),因?yàn)檫@樣的調(diào)用永遠(yuǎn)不會(huì)轉(zhuǎn)向當(dāng)前執(zhí)行的析構(gòu)函數(shù)或構(gòu)造函數(shù)更深層的派生類中執(zhí)行。

posted on 2007-04-27 22:37 ★ROY★ 閱讀(1535) 評(píng)論(4)  編輯 收藏 引用 所屬分類: Effective C++

評(píng)論

# re: 【翻譯】Effective C++ (第9條:永遠(yuǎn)不要在構(gòu)造或析構(gòu)的過(guò)程中調(diào)用虛函數(shù))  回復(fù)  更多評(píng)論   

不錯(cuò), 最近也在看Effective C++
2007-04-28 13:01 | Galaxy

# re: 【翻譯】Effective C++ (第9條:永遠(yuǎn)不要在構(gòu)造或析構(gòu)的過(guò)程中調(diào)用虛函數(shù))[未登錄](méi)  回復(fù)  更多評(píng)論   

不是嚴(yán)格按照effective c++的那些item順序來(lái)翻譯的吧~

是說(shuō)怎么不對(duì)應(yīng)呢
2007-04-29 11:58 | recorder

# re: 【翻譯】Effective C++ (第9條:永遠(yuǎn)不要在構(gòu)造或析構(gòu)的過(guò)程中調(diào)用虛函數(shù))  回復(fù)  更多評(píng)論   

我翻的是,Effective C++第三版,您看的可能是第二版。所以有些不一樣。第三版比第二版要難一些。像/**/和//哪個(gè)好這樣的問(wèn)題第三版中就忽略了。Meyers先生可能還會(huì)寫(xiě)第四版,估計(jì)那時(shí)候難度又要上一個(gè)臺(tái)階。
2007-04-29 16:55 | ★ROY★

# re: 【翻譯】Effective C++ (第9條:永遠(yuǎn)不要在構(gòu)造或析構(gòu)的過(guò)程中調(diào)用虛函數(shù))[未登錄](méi)  回復(fù)  更多評(píng)論   

呵呵,是說(shuō)呢。寫(xiě)得不錯(cuò),繼續(xù)努力~ 偶會(huì)一直關(guān)注~
2007-05-05 17:59 | recorder
青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            国产精品99久久久久久www| 欧美fxxxxxx另类| 久久视频一区| 久久免费精品视频| 在线日韩日本国产亚洲| 久久夜色精品亚洲噜噜国产mv| 久热精品在线| 欧美激情a∨在线视频播放| 欧美精品播放| 亚洲夫妻自拍| 一区二区三区无毛| 欧美制服丝袜第一页| 午夜影院日韩| 久久午夜羞羞影院免费观看| av成人手机在线| 红桃视频一区| 亚洲视频香蕉人妖| 亚洲国产日韩欧美一区二区三区| 亚洲欧美一区二区精品久久久 | 欧美二区在线播放| 欧美激情第8页| 久久亚洲精品伦理| 国产精品草草| 亚洲欧洲精品一区二区精品久久久| 欧美成人午夜免费视在线看片| 欧美一级视频免费在线观看| 欧美日韩亚洲成人| 亚洲国产精品成人综合| 激情综合五月天| 亚洲欧美综合精品久久成人| 一区二区三区高清| 性色av一区二区三区在线观看| 欧美不卡在线视频| 免费h精品视频在线播放| 国产一区二区成人| 欧美一区二区三区视频| 欧美在线视频免费| 国产婷婷色一区二区三区| 亚洲一区二区欧美日韩| 亚洲性线免费观看视频成熟| 欧美日韩一区二区在线观看| 亚洲日本aⅴ片在线观看香蕉| 亚洲精品资源美女情侣酒店| 美日韩精品免费观看视频| 久久综合久久综合这里只有精品 | 久久精品国产亚洲一区二区| 国产精品乱码一区二三区小蝌蚪| 亚洲一区二区三区高清 | 欧美日韩国产a| 午夜精品久久久久久久白皮肤| 久久亚洲私人国产精品va媚药| 亚洲国产裸拍裸体视频在线观看乱了中文 | 欧美激情视频在线免费观看 欧美视频免费一 | 欧美中文字幕第一页| 欧美高清视频在线 | 欧美一区二区私人影院日本| 女仆av观看一区| 欧美中文字幕精品| 国产模特精品视频久久久久| 亚洲欧美变态国产另类| 久久久久久97三级| 亚洲精品一区二区三区四区高清 | 性欧美精品高清| 亚洲第一福利社区| 国产欧美精品在线播放| 狂野欧美激情性xxxx欧美| 亚洲欧洲日本专区| 老司机精品视频一区二区三区| 一区二区三区|亚洲午夜| 亚洲精品国产精品国自产观看浪潮| 国产精品私拍pans大尺度在线| 欧美福利一区| 欧美成人精品福利| 免费不卡视频| 欧美福利电影在线观看| 免费观看在线综合色| 欧美国产亚洲精品久久久8v| 久久久久久色| 欧美xx69| 欧美日韩亚洲激情| 国产精品久久一卡二卡| 国产欧美亚洲视频| 亚洲人久久久| 久热综合在线亚洲精品| 久久深夜福利| 日韩一区二区电影网| 欧美主播一区二区三区| 亚洲肉体裸体xxxx137| 欧美日韩一区二区欧美激情 | 狠狠干成人综合网| 夜夜嗨av色一区二区不卡| 国产精品美女久久久| 亚洲在线中文字幕| 亚洲国产精品一区二区第一页 | 亚洲综合不卡| 一区二区日韩免费看| 亚洲国产精品久久久久婷婷884 | 欧美日韩不卡视频| 最新国产成人在线观看| 免费亚洲一区| 葵司免费一区二区三区四区五区| 亚洲福利国产精品| 亚洲第一页自拍| 雨宫琴音一区二区在线| 黄色精品免费| 亚洲大胆在线| 在线观看不卡av| 亚洲福利视频在线| 亚洲免费电影在线观看| 尤物网精品视频| 国产一区二区三区久久| 亚洲电影av| 国产精品sm| 亚洲一品av免费观看| 久久国产直播| 欧美一区三区三区高中清蜜桃| 亚洲一级黄色| 欧美国产成人精品| 国产精品久久久久77777| 亚洲视频在线观看三级| 国产精品素人视频| 午夜精品福利电影| 久久久久久一区二区| 女人天堂亚洲aⅴ在线观看| 亚洲国产美国国产综合一区二区| 欧美成人精品福利| 中国成人亚色综合网站| 亚洲视频在线看| 99视频国产精品免费观看| 麻豆成人av| 一区二区三区欧美日韩| 欧美国产极速在线| 性色av一区二区三区| 欧美日本网站| 亚洲福利视频专区| 久久亚洲私人国产精品va| 中国女人久久久| 欧美精品亚洲精品| 亚洲午夜黄色| 亚洲欧美综合| 亚洲国产精品成人综合| 久久成人精品无人区| 久久久精品网| 在线视频成人| 欧美国产丝袜视频| 欧美 日韩 国产 一区| 性亚洲最疯狂xxxx高清| 黄色小说综合网站| 亚洲精选国产| 国产日韩精品视频一区二区三区| 久久精品日产第一区二区| 亚洲综合日韩中文字幕v在线| 欧美在线网址| 国产伦精品一区二区三区高清版| 国产香蕉97碰碰久久人人| 美女国内精品自产拍在线播放| 亚洲三级视频在线观看| 久久久www成人免费毛片麻豆| 夜夜爽夜夜爽精品视频| 韩国精品在线观看| 国产精品亚洲美女av网站| 欧美黄在线观看| 久久三级视频| 欧美在线影院在线视频| 亚洲永久免费观看| 一区二区av| 亚洲伦理在线免费看| 欧美寡妇偷汉性猛交| 欧美 日韩 国产 一区| 久久国产精彩视频| 新片速递亚洲合集欧美合集| 亚洲香蕉网站| 亚洲视频在线一区观看| 亚洲另类黄色| 亚洲欧洲日韩女同| 亚洲激情网址| 亚洲激情一区二区| 亚洲精品极品| 亚洲免费观看视频| 夜夜嗨av一区二区三区网站四季av | 激情久久五月天| 欧美成人综合在线| 国产一区二区欧美日韩| 99综合精品| 亚洲自拍都市欧美小说| 久久婷婷av| 亚洲高清视频的网址| 国产三级精品三级| 日韩天堂在线观看| 久久av在线看| 亚洲精品一二| 国产精品久久久爽爽爽麻豆色哟哟| 亚洲电影一级黄| 亚洲欧美大片| 小黄鸭精品aⅴ导航网站入口| 精品999久久久| 国产精品女人久久久久久| 久久精品亚洲一区| 久久婷婷国产综合国色天香| 国产在线观看一区|