<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                企業??AI智能體構建引擎,智能編排和調試,一鍵部署,支持知識庫和私有化部署方案 廣告
                # Item 9: 絕不要在 construction(構造)或 destruction(析構)期間調用 virtual functions(虛擬函數) 作者:Scott Meyers 譯者:fatalerror99 (iTePub's Nirvana) 發布:http://blog.csdn.net/fatalerror99/ 我以這個概述開始:你不應該在 construction(構造)或 destruction(析構)期間調用 virtual functions(虛擬函數),因為這樣的調用不會如你想象那樣工作,而且它們做的事情保證會讓你很郁悶。如果你轉為 Java 或 C# 程序員,也請你密切關注本 Item,因為在 C++ 急轉彎的地方,那些語言也緊急轉了一個彎。 假設你有一套模擬股票交易的 class hierarchy(類繼承體系),例如,購入訂單,出售訂單等。對于這樣的交易來說可供審查是非常重要的,所每次一個交易對象被創建,在一個審查日志中就需要創建一個相應的條目。下面是一個看起來似乎合理的解決問題的方法: ``` class Transaction { // base class for all public: // transactions Transaction(); virtual void logTransaction() const = 0; // make type-dependent // log entry ... }; Transaction::Transaction() // implementation of { // base class ctor ... logTransaction(); // as final action, log this } // transaction class BuyTransaction: public Transaction { // derived class public: virtual void logTransaction() const; // how to log trans- // actions of this type ... }; class SellTransaction: public Transaction { // derived class public: virtual void logTransaction() const; // how to log trans- // actions of this type ... }; ``` 考慮執行這行代碼時會發生什么: ``` BuyTransaction b; ``` 很明顯一個 BuyTransaction 的 constructor(構造函數)會被調用,但是首先,一個 Transaction 的 constructor(構造函數)必須先被調用,derived class objects(派生類對象)中的 base class parts(基類構件)先于 derived class parts(派生類構件)被構造。Transaction 的 constructor(構造函數)的最后一行調用 virtual functions(虛擬函數) logTransaction,但是結果會讓你大吃一驚,被調用的 logTransaction 版本是在 Transaction 中的那一個,而不是 BuyTransaction 中的那一個——即使被創建的 object (對象)類型是 BuyTransaction。base class construction(基類構造)期間,virtual functions(虛擬函數)從來不會 go down(向下匹配)到 derived classes(派生類)。取而代之的是,那個 object (對象)的行為好像它就是 base type(基類型)。非正式地講,base class construction(基類構造)期間,virtual functions(虛擬函數)被禁止。 這個表面上看起來匪夷所思的行為存在一個很好的理由。因為 base class constructors(基類構造函數)在 derived class constructors(派生類構造函數)之前執行,當 base class constructors(基類構造函數)運行時,derived class data members(派生類數據成員)還沒有被初始化。如果 base class construction(基類構造)期間 virtual functions(虛擬函數)的調用 went down(向下匹配)到 derived classes(派生類),derived classes(派生類)的函數差不多總會涉及到 local data members(局部數據成員),但是那些 data members(數據成員)至此還沒有被初始化。這就會為 undefined behavior(未定義行為)和通宵達旦的調試噩夢開了一張通行證。調用涉及到一個 object(對象)還沒有被初始化的構件自然是危險的,所以 C++ 告訴你此路不通。 實際上還有比這更基本的原理。在一個 derived class object(派生類對象)的 base class construction(基類構造)期間,object(對象)的類型是 base class(基類)的類型。不僅 virtual functions(虛擬函數)會解析到 base class(基類),而且用到 runtime type information(運行時類型信息)的語言構件(例如,dynamic_cast(參見 Item 27)和 typeid),也會將那個 object(對象)視為 base class type(基類類型)。在我們的例子中,當 Transaction 的 constructor(構造函數)運行到初始化一個 BuyTransaction object(對象)的 base class(基類)部分時,那個 object(對象)的是 Transaction 類型。C++ 的每一個構件將以如下眼光來看待它,而且這種看法是合理的:這個 object(對象)的 BuyTransaction-specific 的構件還沒有被初始化,所以對它們視若無睹是最安全的。直到 derived class constructor(派生類構造函數)的執行開始之前,一個 object(對象)不會成為一個 derived class object(派生類對象)。 同樣的推理也適用于 destruction(析構)。一旦 derived class destructor(派生類析構函數)運行,這個 object(對象)的 derived class data members(派生類數據成員)就呈現為未定義的值,所以 C++ 就將它們視為不再存在。在進入 base class destructor(基類析構函數)時,這個 object(對象)就成為一個 base class object(基類對象),C++ 的所有構件—— virtual functions(虛擬函數),dynamic_casts 等——都以此看待它。 在上面的示例代碼中,Transaction 的 constructor(構造函數)造成了對一個 virtual functions(虛擬函數)的一次直接調用,是對本 Item 的指導建議的顯而易見的違背。這一違背是如此顯見,以致一些編譯器會給出一個關于它的警告。(另一些則不會。參見 Item 53 對于警告的討論。)即使沒有這樣的一個警告,這個問題也幾乎肯定會在運行之前暴露出來,因為 logTransaction 函數在 Transaction 中是 pure virtual(純虛擬)的。除非它被定義(不太可能,但確實可能——參見 Item 34),否則程序將無法連接:連接程序無法找到 Transaction::logTransaction 的必要的實現。 在 construction(構造)或 destruction(析構)期間調用 virtual functions(虛擬函數)的問題并不總是如此容易被察覺。如果 Transaction 有多個 constructors(構造函數),每一個都必須完成一些相同的工作,軟件工程為避免代碼重復,將共通的 initialization(初始化)代碼,包括對 logTransaction 的調用,放入一個 private non-virtual initialization function(私有非虛擬初始化函數)中,叫做 init: ``` class Transaction { public: Transaction() { init(); } // call to non-virtual... virtual void logTransaction() const = 0; ... private: void init() { ... logTransaction(); // ...that calls a virtual! } }; ``` 這個代碼在概念上和早先那個版本相同,但是它更陰險,因為一般來說它會躲過編譯器和連接程序的抱怨。在這種情況下,因為 logTransaction 在 Transaction 中是 pure virtual(純虛的),在 pure virtual(純虛)被調用時,大多數 runtime systems(運行時系統)會異常中止那個程序(一般會對此結果給出一條消息)。然而,如果 logTransaction 在 Transaction 中是一個 "normal" virtual function(“常規”虛擬函數)(也就是說,not pure virtual(非純虛擬的)),而且帶有一個實現,那個版本將被調用,程序會繼續一路小跑,讓你想象不出為什么在 derived class object(派生類對象)被創建的時候會調用 logTransaction 的錯誤版本。避免這個問題的唯一辦法就是確保你的 constructors(構造函數)或 destructors(析構函數)決不在被創建或析構的 object(對象)上調用 virtual functions(虛擬函數),它們所調用的全部函數也要服從同樣的約束。 但是,你如何確保在每一次 Transaction hierarchy(繼承體系)中的一個 object(對象)被創建時,都會調用 logTransaction 的正確版本呢?顯然,在 Transaction constructor(s)(構造函數)中在這個 object(對象)上調用 virtual functions(虛擬函數)的做法是錯誤的。 有不同的方法來解決這個問題。其中之一是將 Transaction 中的 logTransaction 轉變為一個 non-virtual function(非虛擬函數),這就需要 derived class constructors(派生類構造函數)將必要的日志信息傳遞給 Transaction constructor(構造函數)。那個函數就可以安全地調用 non-virtual(非虛擬)的 logTransaction。如下: ``` class Transaction { public: explicit Transaction(const std::string& logInfo); void logTransaction(const std::string& logInfo) const; // now a non- // virtual func ... }; Transaction::Transaction(const std::string& logInfo) { ... logTransaction(logInfo); // now a non- } // virtual call class BuyTransaction: public Transaction { public: BuyTransaction( parameters ) : Transaction(createLogString( parameters )) // pass log info { ... } // to base class ... // constructor private: static std::string createLogString( parameters ); }; ``` 換句話說,由于你不能在 base classes(基類)的 construction(構造)過程中使用 virtual functions(虛擬函數)向下匹配,你可以改為讓 derived classes(派生類)將必要的構造信息上傳給 base class constructors(基類構造函數)作為補償。 在此例中,注意 BuyTransaction 中那個 (private) static 函數 createLogString 的使用。使用一個輔助函數創建一個值傳遞給 base class constructors(基類構造函數),通常比通過在 member initialization list(成員初始化列表)給 base class(基類)它所需要的東西更加便利(也更加具有可讀性)。將那個函數做成 static,就不會有偶然觸及到一個新生的 BuyTransaction object(對象)的 as-yet-uninitialized data members(仍未初始化的數據成員)的危險。這很重要,因為實際上那些 data members(數據成員)處在一個未定義狀態,這就是為什么在 base class(基類)construction(構造)和 destruction(析構)期間調用 virtual functions(虛擬函數)不能首先向下匹配到 derived classes(派生類)的原因。 Things to Remember * 在 construction(構造)或 destruction(析構)期間不要調用 virtual functions(虛擬函數),因為這樣的調用不會轉到比當前執行的 constructor(構造函數)或 destructor(析構函數)所屬的 class(類)更深層的 derived class(派生類)。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看