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

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                # [20] 繼承 — 虛函數 ## FAQs in section [20]: * [20.1] 什么是“虛成員函數”? * [20.2] C++ 怎樣同時實現動態綁定和靜態類型? * [20.3] 虛成員函數和非虛成員函數調用方式有什么不同? * [20.4] 析構函數何時該時虛擬的? * [20.5] 什么是“虛構造函數(`virtual` constructor)”? ## 20.1 什么是“虛成員函數”? 從面向對象觀點來看,它是 C++ 最重要的特征:[6.8], [6.9]. 虛函數允許派生類取代基類所提供的實現。編譯器確保當對象為派生類時,取代者(譯注:即派生類的實現)總是被調用,即使對象是使用基類指針訪問而不是派生類的指針。這樣就允許基類的算法被派生類取代,即使用戶不知道派生類的細節。 派生類可以完全地取代基類成員函數(覆蓋(override)),也可以部分地取代基類成員函數(增大(augment))。如果愿意的話,后者由派生類成員函數調用基類成員函數來完成。 ## 20.2 C++ 怎樣同時實現動態綁定和靜態類型? 當你有一個對象的指針,而對象實際是該指針類型的派生類(例如:一個 `Vehicle*`指針實際指向一個Car 對象)。由此有兩種類型:指針的(靜態)類型(在此是`Verhicle`),和指向的對象的(動態)類型(在此是Car)。 _靜態類型_意味著成員函數調用的合法性被盡可能早地檢查:編譯器在編譯時。編譯器用指針的靜態類型決定成員函數調用是否合法。如果指針類型能夠處理成員函數,那么指針所指對象當然能很好的處理它。例如,如果?`Vehicle`?有某個成員函數,則由于`Car`是一種`Vehicle`,那么`Car`?當然也有該成員函數。 _動態綁定_意味著成員函數調用的代碼地址在最終時刻才被決定:基于運行時的對象動態類型。因為綁定到實際被調用的代碼這個過程是動態完成的(在運行時),所以被稱為“動態綁定”。動態綁定是虛函數導致的結果之一。 ## 20.3 虛成員函數和非虛成員函數調用方式有什么不同? 非虛成員函數是靜態確定的。也就是說,該成員函數(在編譯時)被靜態地選擇,該選擇基于指象對象的指針(或引用)的類型。 相比而言,虛成員函數是動態確定的(在運行時)。也就是說,成員函數(在運行時)被動態地選擇,該選擇基于對象的類型,而不是指向該對象的指針/引用的類型。這被稱作“動態綁定”。大多數的編譯器使用以下的一些的技術:如果對象有一個或多個虛函數,編譯器將一個 隱藏的指針放入對象,該指針稱為“virtual-pointor”或“v-pointer”。這個v-pointer指向一個全局表,該表稱為“虛函數表(virtural-table)”或“v-table”。 編譯器為每個含有至少一個虛函數的類創建一個v-table。例如,如果`Cirle`類有虛函數d`draw()`、`move()` 和 `resize()`,那么將有且只有一個和Cricle類相關的v-table,即使有一大堆Circle對象。并且每個?`Circle`對象的?v-poiner將指向?`Circle`的這個?v-table。該?v-table自己有指向類的各個虛函數的指針。例如,`Circle`?的v-table?會有三個指針:一個指向`Circle::draw()`,一個指向?`Circle::move()`,還有一個指向`Circle::resize()`。 在分發一個虛函數時,運行時系統跟隨對象的?v-pointer找到類的?v-table,然后跟隨v-table中適當的項找到方法的代碼。 以上技術的空間開銷是存在的:每個對象一個額外的指針(僅僅對于需要動態綁定的對象),加上每個方法一個額外的指針(僅僅對于虛方法)。時間開銷也是有的:和普通函數調用比較,虛函數調用需要兩個額外的步驟(得到v-pointer的值,得到方法的地址)。由于編譯器在編譯時就通過指針類型解決了非虛函數的調用,所以這些開銷不會發生在非虛函數上。 注意:由于沒有涉及諸如多繼承,虛繼承,RTTI等內容,也沒有涉及諸如page fault,通過指向函數的指針調用函數等空間/時間論的內容,所以以上討論是相當簡單的。如果你想知道其他的內容,請詢問?_[`comp.lang.c++`](news:comp.lang.c++)_;而不要給我發E-MAIL! ## 20.4 析構函數何時該時虛擬的? 當你可能通過基類指針刪除派生類對象時。 虛函數綁定到對象的類的代碼,而不是指針/引用的類。如果基類有虛析構函數,`delete?basePtr`時(譯注:即基類指針),`*basePtr`?的對象類型的析構函數被調用,而不是該指針的類型的析構函數。這通常是一件好事情。 _TECHNO-GEEK WARNING; PUT YOUR PROPELLER HAT ON._ 從技術上來說,如果你打算允許其他人通過基類指針調用對象的析構函數(通過`delete`這樣做是正常的),并且被析構的對象是有重要的析構函數的派生類的對象,就需要讓基類的析構函數成為虛擬的。如果一個類有顯式的析構函數,或者有成員對象,該成員對象或基類有重要的析構函數,那么這個類就有重要的析構函數。(注意這是一個遞歸的定義(例如,某個具有重要析構函數的類,它有一個成員對象(它有基類(該基類有成員對象(它有基類(該基類有顯式的析構函數)))))) _END TECHNO-GEEK WARNING; REMOVE YOUR PROPELLER HAT_ 如果你對以上的規則理解有困難,試試這個簡單的:類應該有虛析構函數,除非這個類沒有虛函數。原理:如果有虛函數,說明你想通過基類指針來使用派生對象,并且你所可能做的事情之中,可能包含了調用析構函數(通常通過`delete`隱含完成)。一旦你在類中加上了一個虛函數,你就已經需要為每一個對象支付空間代價(每個對象一個指針;注意這是理論上的編譯器特性;實際上每個編譯器都是這樣做的),所以這時使析構函數成為虛擬的通常不會額外付出什么。 ## 20.5 什么是“虛構造函數(`virtual` constructor)”? 一種允許你做一些?C++?不直接支持的事情的用法。 你可能通過虛函數?`virtual` `clone()`(對于拷貝構造函數)或虛函數 `virtual` `create()`(對于默認構造函數),得到虛構造函數產生的效果。 ``` ?class?Shape?{ ?public: ???virtual?~Shape()?{?}?????????????????//?虛析構函數 ???virtual?void?draw()?=?0;?????????????//?純虛函數 ???virtual?void?move()?=?0; ???//?... ???virtual?Shape*?clone()??const?=?0;???//?使用拷貝構造函數_ ???virtual?Shape*?create()?const?=?0;???//?使用默認構造函數 ?}; ?class?Circle?:?public?Shape?{ ?public: ???Circle*?clone()??const?{?return?new?Circle(*this);?} ???Circle*?create()?const?{?return?new?Circle();??????} ???//?... ?}; ``` 在?`clone()`?成員函數中,代碼?`new?Circle(*this)` 調用?`Circle` 的拷貝構造函數來復制`this`的狀態到新創建的`Circle`對象。在?`create()`成員函數中,代碼?`new?Circle()`?調用`Circle`的默認構造函數。 用戶將它們看作“虛構造函數”來使用它們: ``` ?void?userCode(Shape&?s) ?{ ???Shape*?s2?=?s.clone(); ???Shape*?s3?=?s.create(); ???//?... ???delete?s2;????//?在此處,你可能需要虛析構函數 ???delete?s3; ?} ``` 這個函數將正確工作,而不管?`Shape`?是一個`Circle`,`Square`,或是其他種類的?`Shape`,甚至它們還并不存在。 注意:成員函數`Circle`'s `clone()`的返回值類型故意與成員函數`Shape`'s `clone()`的不同。這種特征被稱為“協變的返回類型”,該特征最初并不是語言的一部分。如果你的編譯器不允許在`Circle`類中這樣聲明`Circle*?clone()?const`(如,提示“The return type is different”或“The member function's type differs from the base class virtual function by return type alone”),說明你的編譯器陳舊了,那么你必須改變返回類型為`Shape*。`
                  <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>

                              哎呀哎呀视频在线观看