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

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                # Item 7: 在 polymorphic base classes(多態基類)中將 destructors(析構函數)聲明為 virtual(虛擬) 作者:Scott Meyers 譯者:fatalerror99 (iTePub's Nirvana) 發布:http://blog.csdn.net/fatalerror99/ 有很多方法取得時間,所以有必要建立一個 TimeKeeper base class(基類),并為不同的計時方法建立 derived classes(派生類): ``` class TimeKeeper { public: TimeKeeper(); ~TimeKeeper(); ... }; class AtomicClock: public TimeKeeper { ... }; class WaterClock: public TimeKeeper { ... }; class WristWatch: public TimeKeeper { ... }; ``` 很多客戶只是想簡單地取得時間而不關心如何計算的細節,所以一個 factory function(工廠函數)——返回 a base class pointer to a newly-created derived class object(一個指向新建派生類對象的基類指針)的函數——可以被用來返回一個指向 timekeeping object(計時對象)的指針: ``` TimeKeeper* getTimeKeeper(); // returns a pointer to a dynamic- // ally allocated object of a class // derived from TimeKeeper ``` 與 factory function(工廠函數)的慣例一致,getTimeKeeper 返回的 objects(對象)是建立在 heap(堆)上的,所以為了避免泄漏內存和其它資源,每一個返回的 objects(對象)被完全 deleted 是很重要的。 ``` TimeKeeper *ptk = getTimeKeeper(); // get dynamically allocated object // from TimeKeeper hierarchy ... // use it delete ptk; // release it to avoid resource leak ``` Item 13 解釋了為什么依賴客戶執行刪除任務是 error-prone(錯誤傾向),Item 18 解釋了 factory function(工廠函數)的 interface(接口)應該如何改變以防止普通的客戶錯誤,但這些在這里都是次要的,因為在這個 Item 中,我們將精力集中于上面的代碼中一個更基本的缺陷:即使客戶做對了每一件事,也無法預知程序將如何運轉。 問題在于 getTimeKeeper 返回一個 pointer to a derived class object(指向派生類對象的指針)(比如 AtomicClock),那個 object(對象)經由一個 base class pointer(基類指針)(也就是一個 TimeKeeper\* pointer)被刪除,而且這個 base class(基類)(TimeKeeper) 有一個 non-virtual destructor(非虛擬析構函數)。禍端就在這里,因為 C++ 規定:當一個 derived class object(派生類對象)通過使用一個 pointer to a base class with a non-virtual destructor(指向帶有非虛擬析構函數的基類的指針)被刪除,則結果是未定義的。運行時比較典型的后果是 derived part of the object(這個對象的派生部分)不會被析構。如果 getTimeKeeper 返回一個指向 AtomicClock object(對象)的指針,則 object(對象)的 AtomicClock 部分(也就是在 AtomicClock class 中聲明的 data members(數據成員))很可能不會被析構,AtomicClock 的 destructor(析構函數)也不會運行。然而,base class part(基類部分)(也就是 TimeKeeper 部分)很可能已被析構,這就導致了一個古怪的 "partially destroyed" object(“部分被析構”對象)。這是一個導致泄漏資源,破壞數據結構以及消耗大量調試時間的絕妙方法。 消除這個問題很簡單:給 base class(基類)一個 virtual destructor(虛擬析構函數)。于是,刪除一個 derived class object(派生類對象)的時候就有了你所期望的行為。將析構 entire object(整個對象),包括全部的 derived class parts(派生類構件): ``` class TimeKeeper { public: TimeKeeper(); virtual ~TimeKeeper(); ... }; TimeKeeper *ptk = getTimeKeeper(); ... delete ptk; // now behaves correctly ``` 類似 TimeKeeper 的 base classes(基類)一般都包含除了 destructor(析構函數)以外的其它 virtual functions(虛擬函數),因為 virtual functions(虛擬函數)的目的就是允許 derived class implementations(派生類實現)的定制化(參見 Item 34)。例如,TimeKeeper 可以有一個 virtual functions(虛擬函數)getCurrentTime,它在各種不同的 derived classes(派生類)中有不同的實現。幾乎所有擁有 virtual functions(虛擬函數)的 class(類)差不多都應該有一個 virtual destructor(虛擬析構函數)。 如果一個 class(類)不包含 virtual functions(虛擬函數),這經常預示不打算將它作為 base class(基類)使用。當一個 class(類)不打算作為 base class(基類)時,將 destructor(析構函數)虛擬通常是個壞主意。考慮一個表現二維空間中的點的 class(類): ``` class Point { // a 2D point public: Point(int xCoord, int yCoord); ~Point(); private: int x, y; }; ``` 如果一個 int 占用 32 bits,一個 Point object 正好適用于 64-bit 的寄存器。而且,這樣一個 Point object 可以被作為一個 64-bit 的量傳遞給其它語言寫的函數,比如 C 或者 FORTRAN。如果 Point 的 destructor(析構函數)被虛擬,情況就完全不一樣了。 virtual functions(虛擬函數)的實現要求 object(對象)攜帶額外的信息,這些信息用于在運行時確定該 object(對象)應該調用哪一個 virtual functions(虛擬函數)。典型情況下,這一信息具有一種被稱為 vptr ("virtual table pointer")(虛擬函數表指針)的指針的形式。vptr 指向一個被稱為 vtbl ("virtual table")(虛擬函數表)的 array of function pointers(函數指針數組),每一個帶有 virtual functions(虛擬函數)的 class(類)都有一個相關聯的 vtbl。當在一個 object(對象)上調用 virtual functions(虛擬函數)時,實際的被調用函數通過下面的步驟確定:找到 object(對象)的 vptr 指向的 vtbl,然后在 vtbl 中尋找合適的 function pointer(函數指針)。 virtual functions(虛擬函數)是如何實現的細節并不重要。重要的是如果 Point class 包含一個 virtual functions(虛擬函數),這個類型的 object(對象)的大小就會增加。在一個 32-bit 架構中,它們將從 64 bits(相當于兩個 ints)長到 96 bits(兩個 ints 加上 vptr);在一個 64-bit 架構中,它們可能從 64 bits 長到 128 bits,因為在這樣的架構中指針的大小是 64 bits 的。為 Point 加上 vptr 將會使它的大小增長 50-100%!Point object(對象)不再適合 64-bit 寄存器。而且,Point object(對象)在 C++ 和其它語言(比如 C)中,看起來不再具有相同的結構,因為它們在其它語言中的對應物沒有 vptr。結果,Points 不再可能傳入其它語言寫成的函數或從其中傳出,除非你為 vptr 做出明確的補償,而這是它自己的實現細節并因此失去可移植性。 最終結果就是無故地將所有 destructors(析構函數)聲明為 virtual(虛擬),和從不把它們聲明為 virtual(虛擬)一樣是錯誤的。實際上,很多人總結過這條規則:declare a virtual destructor in a class if and only if that class contains at least one virtual function(當且僅當一個類中包含至少一個虛擬函數時,則在類中聲明一個虛擬析構函數)。 甚至在完全沒有 virtual functions(虛擬函數)時,也有可能糾纏于 non-virtual destructor(非虛擬析構函數)問題。例如,標準 string 類型不包含 virtual functions(虛擬函數),但是被誤導的程序員有時將它當作 base class(基類)使用: ``` class SpecialString: public std::string { // bad idea! std::string has a ... // non-virtual destructor }; ``` 一眼看上去,這可能無傷大雅,但是,如果在程序的某個地方因為某種原因,你將一個 pointer-to-SpecialString(指向 SpecialString 的指針)轉型為一個 pointer-to-string(指向 string 的指針),然后你將 delete 施加于這個 string pointer(指針),你就立刻被放逐到 undefined behavior(未定義行為)的領地: ``` SpecialString *pss = new SpecialString("Impending Doom"); std::string *ps; ... ps = pss; // SpecialString* → std::string* ... delete ps; // undefined! In practice, // *ps's SpecialString resources // will be leaked, because the // SpecialString destructor won't // be called. ``` 同樣的分析可以適用于任何缺少 virtual destructor(虛擬析構函數)的 class(類),包括全部的 STL container(容器)類型(例如,vector,list,set,tr1::unordered_map(參見 Item 54)等)。如果你受到從 standard container(標準容器)或任何其它帶有 non-virtual destructor(非虛擬析構函數)的 class(類)繼承的誘惑,一定要挺住!(不幸的是,C++ 不提供類似 Java 的 final classes(類)或 C# 的 sealed classes(類)的 derivation-prevention mechanism(防派生機制)。) 有時候,給一個 class(類)提供一個 pure virtual destructor(純虛擬析構函數)能提供一些便利。回想一下,pure virtual functions(純虛擬函數)導致 abstract classes(抽象類)——不能被實例化的 classes(類)(也就是說你不能創建這個類型的 objects(對象))。然而,有時候,你有一個 class(類),你希望它是抽象的,但沒有任何 pure virtual functions(純虛擬函數)。怎么辦呢?因為一個 abstract classes(抽象類)注定要被用作 base class(基類),又因為一個 base class(基類)應該有一個 virtual destructor(虛擬析構函數),還因為一個 pure virtual functions(純虛擬函數)產生一個 abstract classes(抽象類),好了,解決方案很簡單:在你想要變成抽象的 class(類)中聲明一個 pure virtual destructor(純虛擬析構函數)。這是一個例子: ``` class AWOV { // AWOV = "Abstract w/o Virtuals" public: virtual ~AWOV() = 0; // declare pure virtual destructor }; ``` 這個 class(類)有一個 pure virtual functions(純虛擬函數),所以它是抽象的,又因為它有一個 virtual destructor(虛擬析構函數),所以你不必擔心析構函數問題。這是一個螺旋。然而,你必須為 pure virtual destructor(純虛擬析構函數)提供一個 definition(定義): ``` AWOV::~AWOV() {} // definition of pure virtual dtor ``` destructors(析構函數)的工作方式是:most derived class(層次最低的派生類)的 destructor(析構函數)最先被調用,然后調用每一個 base class(基類)的 destructors(析構函數)。編譯器會生成一個從它的 derived classes(派生類)的 destructors(析構函數)對 ~AWOV 的調用,所以你不得不確保為函數提供一個函數體。如果你不這樣做,連接程序會提出抗議。 為 base classes(基類)提供 virtual destructor(虛擬析構函數)的規則僅僅適用于 polymorphic base classes(多態基類)—— base classes(基類)被設計成允許通過 base class interfaces(基類接口)對 derived class types(派生類類型)進行操作。TimeKeeper 就是一個 polymorphic base classes(多態基類),因為即使我們只有類型為 TimeKeeper 的 pointers(指針)指向它們的時候,我們也期望能夠操作 AtomicClock 和 WaterClock objects(對象)。 并非所有的 base classes(基類)都被設計用于 polymorphically(多態)。例如,無論是 standard string type(標準 string 類型),還是 STL container types(STL 容器類型)全被設計成 base classes(基類),可沒有哪個是 polymorphic(多態)的。一些 classes(類)雖然被設計用于 base classes(基類),但并非被設計用于 polymorphically(多態)。這樣的 classes(類)——例如 Item 6 中的 Uncopyable 和標準庫中的 input_iterator_tag(參見 Item 47)——沒有被設計成允許經由 base class interfaces(基類接口)對 derived class objects(派生類對象)進行操作。所以它們就不需要 virtual destructor(虛擬析構函數)。 Things to Remember * polymorphic base classes(多態基類)應該聲明 virtual destructor(虛擬析構函數)。如果一個 class(類)有任何 virtual functions(虛擬函數),它就應該有一個 virtual destructor(虛擬析構函數)。 * 不是設計用來作為 base classes(基類)或不是設計用于 polymorphically(多態)的 classes(類)就不應該聲明 virtual destructor(虛擬析構函數)。
                  <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>

                              哎呀哎呀视频在线观看