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

洛譯小筑

別來(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精品视频免费全部在线| 欧美午夜精品伦理| 亚洲一区影院| 香蕉成人久久| 亚洲国产精品99久久久久久久久| 欧美黄色日本| 欧美日韩在线观看一区二区| 亚洲专区免费| 久久久久久9999| 日韩系列在线| 亚洲欧美国产日韩天堂区| 国产一区二区三区最好精华液| 免费在线看一区| 欧美日韩一二区| 美日韩精品视频| 欧美一区二区在线观看| 亚洲一区二区三区色| 国产一二三精品| 亚洲激情女人| 欧美香蕉大胸在线视频观看| 久久视频一区| 欧美三日本三级少妇三2023| 欧美在线一级视频| 欧美黄色大片网站| 久久精品国产69国产精品亚洲| 另类成人小视频在线| 亚洲欧美一区二区三区极速播放| 久久精品国产精品亚洲精品| 夜夜嗨av一区二区三区中文字幕 | 亚洲精品久久| 亚洲制服av| 亚洲最新视频在线| 久久天天综合| 久久国产精品久久久久久久久久 | 午夜视频一区在线观看| 99精品久久| 狂野欧美激情性xxxx欧美| 亚洲欧美久久久久一区二区三区| 久久影院亚洲| 久久先锋影音| 国产欧美日韩一级| 亚洲一级片在线观看| 99ri日韩精品视频| 欧美福利一区二区三区| 老司机免费视频一区二区| 国产精品无码专区在线观看| 日韩一本二本av| 亚洲精品自在在线观看| 美女视频黄a大片欧美| 久久天天综合| 国产综合18久久久久久| 亚洲欧美日韩精品一区二区| 亚洲综合99| 欧美性猛交xxxx乱大交蜜桃| 亚洲人成网站777色婷婷| 亚洲三级性片| 欧美大香线蕉线伊人久久国产精品| 久久综合99re88久久爱| 国内精品亚洲| 久久久久久亚洲综合影院红桃 | 一区二区三区精品国产| 欧美片第一页| 一本在线高清不卡dvd| 99精品欧美一区二区蜜桃免费| 欧美成年人网站| 亚洲黄网站在线观看| 亚洲人成网站999久久久综合| 久久三级视频| 亚洲国产日韩一级| 亚洲免费观看高清在线观看| 欧美日本三级| 一区二区三区欧美激情| 午夜国产精品视频免费体验区| 国产精品久久夜| 性欧美xxxx视频在线观看| 韩日精品视频| 农夫在线精品视频免费观看| 亚洲丶国产丶欧美一区二区三区| 亚洲欧洲综合另类| 欧美性开放视频| 久久不射中文字幕| 亚洲国产精品www| 亚洲午夜免费视频| 国产小视频国产精品| 久久婷婷丁香| 日韩一级裸体免费视频| 欧美伊人久久久久久午夜久久久久| 国产午夜亚洲精品理论片色戒| 久色成人在线| 中国成人亚色综合网站| 久久婷婷人人澡人人喊人人爽| 亚洲国产精品嫩草影院| 国产精品福利网站| 久久久久久亚洲精品杨幂换脸 | 午夜精品久久久久久99热软件| 久色婷婷小香蕉久久| 一本高清dvd不卡在线观看| 国产精品自拍网站| 欧美99在线视频观看| 亚洲一区二区在线视频| 欧美大片一区| 欧美在线播放高清精品| 亚洲欧洲在线一区| 国产亚洲欧美另类中文| 欧美精品高清视频| 久久精品日产第一区二区三区| 亚洲日本成人女熟在线观看| 久久久久88色偷偷免费| 国产精品99久久99久久久二8 | 久久精品人人做人人爽电影蜜月| 亚洲精品久久久蜜桃| 国产欧美日本一区二区三区| 欧美美女bbbb| 麻豆久久精品| 久久不射2019中文字幕| 亚洲网站在线看| 亚洲黄网站在线观看| 裸体一区二区三区| 久久精品视频在线播放| 亚洲一区一卡| 亚洲图片欧美午夜| 亚洲精品乱码久久久久久黑人| 国产日产精品一区二区三区四区的观看方式 | 99视频有精品| 亚洲人成毛片在线播放女女| 国产一区二区三区黄视频| 欧美性视频网站| 欧美日韩在线亚洲一区蜜芽| 免费亚洲网站| 久久久综合免费视频| 久久国产精品高清| 欧美一区二区视频在线观看2020| 亚洲婷婷综合色高清在线 | 欧美成人日韩| 日韩视频免费看| 亚洲激情一区| 亚洲韩国青草视频| 91久久精品国产91久久性色tv| 狠狠做深爱婷婷久久综合一区 | 亚洲电影在线免费观看| 欧美国产精品劲爆| 亚洲成色www久久网站| 欧美激情亚洲一区| 亚洲精品1区| 亚洲精品男同| 一本一本久久a久久精品综合麻豆| 亚洲精品美女| 一区二区三区精品国产| 亚洲视频久久| 午夜亚洲精品| 久久精品人人做人人爽电影蜜月| 久久久国产一区二区三区| 久久婷婷国产综合国色天香| 米奇777超碰欧美日韩亚洲| 欧美成黄导航| 国产精品久久久久久久久借妻| 国产精品亚洲片夜色在线| 国产亚洲精品aa| 亚洲高清影视| 一道本一区二区| 香港成人在线视频| 理论片一区二区在线| 亚洲国产精品久久精品怡红院| 亚洲精品老司机| 亚洲一区制服诱惑| 久久成人18免费观看| 欧美 日韩 国产在线 | 免费在线成人av| 欧美日韩国产成人在线免费 | 欧美激情一区二区三区在线 | 欧美理论电影在线播放| 国产精品日韩在线| 亚洲国产美女精品久久久久∴| 一本色道久久综合亚洲精品婷婷| 欧美一区二区在线播放| 亚洲第一精品影视| 亚洲欧美日韩国产中文| 欧美风情在线观看| 国产亚洲成av人在线观看导航| 亚洲国产精品热久久| 欧美在线观看日本一区| 亚洲激情视频在线播放| 欧美一进一出视频| 欧美国产日本| 精品91在线| 校园春色国产精品| 亚洲欧洲一区二区在线观看| 欧美综合国产精品久久丁香| 欧美视频一区二区三区四区| 亚洲国产1区| 久久成人羞羞网站| 99亚洲视频| 欧美激情一区| 在线观看欧美精品| 久久精品人人| 亚洲综合色网站| 欧美涩涩网站|