轉:Inside the C++ Object Model學習筆記[Chap5]

來源:互聯網
上載者:User
 

第5章構造、析構與拷貝語意學 5.0 引言可以定義和調用一個純虛函數,但是只能被靜態地調用,不能經由虛擬機器制調用,如:class AbstractBase {       public:              virtual void interface() const = 0;};inline void AbstractBase::interface() const { …… }class ConcretDerived : public AbstractBase {       public:              void interface() const;};inline void ConcretDerived::interface() const {       AbstractBase::interface();       //……}但是否這樣做,取決於類的設計者,但是對於純虛解構函式,類設計者一定得定義,因為每一個衍生類別解構函式會被編譯器加以擴充,以靜態調用的方式調用其“每一個虛基類”以及“上一層基類”的解構函式,因而只要缺乏任何一個基類解構函式的定義,連結將會失敗。否則就不要把虛解構函式聲明為純虛。【並不是好的筆記,應該按書中的問題提出到問題解決方式】 5.1 無繼承下的物件建構 5.1.1 Plain OI’ Data形式聲明如下的Point(第一版本),在C++標準中稱之為Plain OI’ Data聲明形式:typedef struct {       float _x, _y, _z;} Point;編譯時間,觀念上編譯器會為Point聲明一個trivial預設建構函式,一個trivial解構函式,一個trivial拷貝建構函式,一個trivial拷貝賦值運算子,取址運算子及其const版六個成員函數。但實際上,編譯器分析這個聲明,並為它貼上Plain OI’ Data標籤。編譯器遇到如下定義Point global;時,觀念上其建構函式和解構函式都會被產生並被調用,即建構函式在程式的起始處而解構函式在exit()(exit()由系統產生,放在main結束之前)處被調用,然而,事實上那些trivial成員要不是沒有被定義,就是沒被調用,程式的行為一如它在C中表現的一樣。對Point *heap = new Point;編譯器會轉換為Point *heap = _new( sizeof( Point ));注意其中並沒有預設建構函式施行於new運算子所傳回的Point對象身上。另外拷貝建構函式,賦值運算子以及解構函式等都一樣並沒有被產生調用,只是涉及到一些簡單的位拷貝操作。【好像Plain OI’ Data適用於C的struct形式】 5.1.2 ADT形式(不含虛函數)將Plain OI’ Data重寫為如下不含虛函數的ADT形式:class Point {       public:              Point(float x = 0.0, float y = 0.0, float z = 0.0) : _x(x), _y(y), _z(z) {}              //no other copy constructor, copy operator or destructor       private:              float _x;              float _y;              float _z;};經過如上封裝,但是類大小並沒有改變,因為對於private,public,protected存取許可權,或者是成員函數的聲明,都不會佔用額外的對象空間。現在定義Point global;將會調用預設的建構函式。而對Point *heap = new Point;則被附加為一個“對預設建構函式有條件的叫用作業”:Point* heap = _new( sizeof( Point ) );if ( heap != 0)      heap->Point::Point();而對於delete heap;並不會導致解構函式調用,因為並沒有明確地提供一個解構函式實體。 5.1.2 ADT形式(含虛函數)為了繼承以及多態,將上述ADT改寫為如下帶有虛函數的類定義:class Point {       public:              Point(float x = 0.0, float y = 0.0 ) : _x(x), _y(y) {}              //no other copy constructor, copy operator or destructor              virtual float z();       protected:              float _x;              float _y;};Point global;Point foobar() {       Point local;       Point *heap = new Point;       *heap = local;       delete heap;       return local;}引入虛函數後帶來的變化:1. 每一個Point對象擁有一個vptr,指向類的vtbl,這個指標提供了虛擬介面的彈性,但是使得每一個對象需要一個額外的指標空間;2. 使得類Point定義將會產生如下的膨脹:① 定義的constructor中被附加了一些代碼,以便將vptr初始化,這些代碼附在任何基類建構函式的調用之後,但是位於任何使用者定義的代碼之前:Point* Point::Point( Point *this, float x, float y) : _x(x), _y(y) {       this->_vptr_Point = _vtbl_Point;       this->_x = x;       this->_y = y;       return this;}② 合成一個拷貝建構函式和一個拷貝賦值運算子,而且其操作不再是trivial,但隱式解構函式仍然是trivial,這種情況下以位位基礎的操作可能給vptr帶來非法設定。inline Point* Point::Point( Point *this, const Point& rhs) {       this->_vptr_Point = _vtbl_Point;       this->_x = rhs.x;       this->_y = rhs.y;       return this;}3. 引入虛函數後操作也會發生變化① 對於Point global;以及Point* heap = new Point;全域對象初始化以及堆對象同ADT的非虛擬函數,調用預設建構函式;② 對於釋放堆對象delete heap;的操作由於沒有定義解構函式實體,同上並不會調用解構函式;③ 對於*heap = local;將會觸發內部合成的拷貝賦值運算子;④ 對於return local;以傳值的方式返回局部對象,將會調用合成的拷貝建構函式,即:Point foobar( Point& _result ) {       Point local;       local.Point::Point(0.0, 0.0);       _result.Point::Point( local );       local.Point::~Point(); //如果定義或合成了的話       return;} 5.2 繼承體系下的物件建構 5.2.1建構函式的擴充概述對於定義一個對象如下:T object;如果T有一個建構函式(不論是使用者提供或由編譯器合成),那麼它會被調用,同時根據類T的繼承體系,編譯器可能做如下的擴充操作:1. 記錄在成員初始化列表中的資料成員初始化操作會被放進建構函式本身,並以成員聲明的順序作為順序;2. 如果有一個成員沒有出現在成員初始化列表中,但他有一個預設建構函式,那麼該預設建構函式被調用;3. 在那之前,如果類對象有vptr,那麼必須被設定初值,指向適當的vtbl;4. 在那之前,所有上一層的基類建構函式被調用,以基類的聲明順序為順序(與在成員初始化列表中的順序沒有關聯): ★ 如果基類被列於成員初始化列表中,那麼任何明確指定的參數都應該傳遞過去; ★ 如果基類沒有被列於成員初始化列表中,而他有預設建構函式,那麼調用預設構建構函式; ★ 如果基類是多重繼承下的第二或後繼的基類,那麼this指標必須做調整;5. 在那之前,所有虛基類建構函式必須被調用,從左至右,從最深到最淺; ★ 如過類被列於成員初始化列表中,那麼如果有任何明確指定的參數都應該傳遞過去,若沒有列於初始化列表中,但類有一個預設建構函式,就調用建構函式; ★ 類中的每一個虛基類subobject的位移量在執行期可被存取; ★ 如果類對象是最底層的類,其建構函式可能被調用,用以支援這個行為的機制也必須放進來。 5.2.2單一非虛擬繼承顯然這是繼承中最為簡單的構造,根據先基類然後類成員(即成員的建構函式)然後類本身(使用者定義的代碼)的構造順序初始化即可。 5.2.2虛擬繼承對於虛擬繼承而言,由於虛擬繼承就是為了避免基類在衍生類別中的多份拷貝問題,對單一虛擬繼承而言就沒有任何好處,反而會付出空間與存取時間的代價,因此一般只考慮多重繼承的情況。虛擬繼承中虛擬基類建構函式的被調用有著明確的定義:只有當一個完整的類對象被定義出來時,它才會被調用;如果對象只是某個完整對象的subobject,它就不會被調用(詳細討論見書P211-213)。 5.2.3 vptr初始化語意學如果叫用作業限制在建構函式或解構函式中直接調用,那麼將每一個叫用作業以靜態方式決議,並不用到虛擬機器制(詳細討論見書P214-215)。決定虛函數名單的關鍵在於虛表vtbl,而vtbl又通過vptr處理。Vptr初始化操作處理在基類建構函式叫用作業之後,但是在程式員供應的代碼或是成員初始化列表中所列的成員初始化之前進行,因此引入vptr初始化後的建構函式執行步驟為:1. 在衍生類別建構函式中,所有虛基類及上一層基類的建構函式被調用;2. 對象的vptr被初始化,指向相關的vtbl;3. 如果有成員初始化列表,那麼在建構函式體內展開;4. 執行自訂的代碼。 5.3 對象拷貝語意學 5.3.1類對象賦值的選擇當設計一個類,並以該類的一個對象賦給另一個類對象時,有如下三種選擇:1. 什麼都不作,實施預設行為,即概念上的memberwise與實現上的bitwise拷貝語意;2. 提供一個顯式copy assignment operator;3. 明確地拒絕賦值操作,可以採用將copy assignment operator聲明為private,並且不提供定義即可。因為聲明為private就不再允許在任何地點(除了成員函數以及友元之中)進行賦值操作;不提供定義則一旦某個成員函數或友元企圖影響一份拷貝,程式在連結時會失敗。 5.3.2 copy assignment語意對於支援的只是一個簡單的拷貝操作,如沒有類成員,沒有指標以及不存在資源分派等,那麼預設的bitwise copy已經足夠而且有效率,沒有必要提供自己的copy assignment operator。但是預設行為導致的語意不安全或不正確時,即發生記憶體流失或者別名化等問題時,需要自己提供一個正確的copy assignment operator。一個類對於預設的copy assignment operator,在以下情況下不會表現bitwise copy語意:1. 類內帶一個成員對象,而其類有一個copy assignment operator,不管這個copy assignment operator是明確定義或者隱式合成的;2. 類的基類有一個copy assignment operator時;3. 類聲明了任何虛函數時;4. 類繼承自一個虛基類,而且不論該基類有沒有copy assignment operator時。 5.3.3繼承下的copy assignment運算子衍生類別的copy assignment operator將會調用基類的copy assignment operator,然後再執行其他增加的操作。copy assignment operator在虛擬繼承情況下行為不佳,需要小心地設計和說明。 5.4 析構語意學如果類沒有定義解構函式,那麼只有在class內帶的成員對象(或基類)擁有解構函式的情況下,編譯器才會自動合成出一個來,否則解構函式被視為不需要,也就不需要被合成及調用,因此對於定義了一個建構函式,那麼就必須提供一個解構函式的想法並不正確。在delete一個對象前沒有強制要求得先將其內容清除乾淨。一個由程式員定義的解構函式被擴充的方式類似建構函式被擴充的方式,但是順序相反:1. 如果對象內帶一個vptr,那麼首先重設指向vtbl;2. 解構函式本身被執行,也就是說vptr會在程式員的代碼執行前被重設;3. 如果類擁有成員類對象,而後者有解構函式,那麼他們以其聲明的相反順序被調用;4. 如果有任何直接的非虛函數基類擁有解構函式,以其聲明的相反順序調用;5. 如果有任何虛基類擁有解構函式,而當前討論的這個類是最尾端的類,那麼他們會以其原來的構造順序的相反順序被調用。
相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.