《深度探索c++ 對象模型》有感之默認構造函數,拷貝構造函數,拷貝運算符的合成

1.理論部分   

1.1前言

        一般情況下,定義一個class的時候,理論上,編譯器可能這個類合成很多東西,有default constructor ,copy constructor ,copy assignment operator,分別是默認構造函數,拷貝構造函數,拷貝指定運算符

1.2不調用構造函數

       至於實際上會不會真正的合成,這要看需不需要,需要,這個詞很多次出現在《深度探索C++對象模型》中,本人認爲,需要的意思就是在沒有這些構造函數的情況下,對類的初始化的完全沒問題的時候,那就不用合成,比如說 bitwise copy (逐個成員的按位複製)這種方法,而且種方法如果可以的話,它的效率可以說是最高的,但是很多情況下,這種簡單的按位複製並不可以完成類的初始化,這個時候,編譯器就要使出新武器了,爲類合成,下面就是bitwise 失效的四種情況:就以合成default constructor這種情況來說明:

1.3 bitwise copy 失效

1.31第一種

    如果A這個類有一個成員變量b,而b是B類的對象,B類有默認構造函數,那麼編譯器就要爲A合成一個默認的構造函數default constructor,可以說是B類的默認構造函數促使編譯器幫A合成一個default constructor,在默認構造函數中來調用B的默認構造函數,

1.32第二種

    如果A是一個派生類父類是D,而D是有默認構造函數的,編譯器也會合成一個默認構造函數給A,可以說,是父類的默認構造函數促使編譯器給A合成的,因爲父類本來就有默認構造函數了,子類必須要有default constructor 來調用父類的default constructor 啊

1.33第三種

    如果A類中有虛函數,爲了實現多態,c++引入了虛函數,由於虛函數是可以有多個版本的,在執行期,纔會決定選擇哪一個版本以實現多態,根據C++對象模型,編譯器會合成一張虛表,表中存放多個虛函數的地址,還要生成一個指針vptr放在對象中,指向虛表,這些指針的指向的操作必須要放在一個默認構造函數,才能完成初始化,編譯器要爲vprt直接初始化,不行,不像int 那樣,直接給個0就完事兒了。

1.34第四種

    如果出現虛基類,如果A同時繼承與B 和C(多繼承),B,C都虛繼承於D,根據C++的規定,虛繼承的D在內存中只能存有一份對象,由BC共享(爲了防止成員的二義性),那麼這個指針的指向就比較複雜了,所以必須要合成default constructor,在這裏邊完成初始化,插入一些支持虛基類,機制的代碼


以上四種情況是 合成 default constructor ,copy constructor, copy assignment constructor,的公共前提

    


2.實際的使用

2.1 應用的時候,產生對象的情況

    什麼情況下會產生類的對象從而可能調用default constructor ,copy constructor, copy assignment constructor這些函數的其中一種呢)呢?有三種情況:

2.11,顯式初始化的時候

     如A a; A a = new A();

 

2.12:函數傳參的時候

    是形參,如a(A a);這個時候,會產生一個類的對象,來接受傳進來的參數,但是,如果參數是指針或者引用類型的話,不會產生新對象,


2.13:函數有返回值的時候 如:

A a()

{

  A a  ;

  /後面N多操作~~~    

   return ;

}

   A b = a();後面這句  A b  = a(); 首先要產生一個對象比如說 A c 來接受a()這個函數的返回值,再把c賦給a;當然,如果返回是指針或者引用的話,不會有新對象出現(雖然如此,但是編譯器還是會進行優化的,叫NRV 優化,插入一個應用接受返回值,以達到減少constructor 的調用和 destructor的調用)

以上三種情況均可產生一個對象


2.21 第一個默認的構造函數

    如果我是這樣寫的,A a;如果我的A這個類本來就沒有這個默認的構造函數而且我的A類裏邊沒有指針,也沒有虛函數,只有一些基本的類型的成員,如int,float,等類型,那麼編譯器會自動幫這些成員初始化,一般int這些類型會設爲0,但是很多情況下,類裏邊都會有指針啊,虛函數啊,還有其他類的成員作爲成員變量,那麼編譯器想要給你直接初始化?想太多,因爲類的對象不是隨隨便便就可以幫你指定一個值的,指針也不能幫你正確的初始化,所以     

     說完默認構造函數,下面看看另外一種對象的初始化調用copy,

2.22第二個:拷貝構造函數

    你的代碼是A a = A(b); 或者,A a = b; 如果是這種寫法,那麼編譯器的首要操作是把b的值按照成員一個一個的初始化,從b中拷貝到a中,這個叫 bitwise copy按位拷貝,把b中的每一個成員都相應的拷貝給a,如果b中的成員是另一個的對象,那麼拷貝的這個成員時候 ,會遞歸的拷貝這個成員的內部的參數,但是,這種按位的拷貝,並不是暢通無阻的,和上邊的那種編譯器默認初始化一樣,在一些情況下,會無用武之地,這個時候,編譯器會合成以外一種構造函數,copy constructor 拷貝構造函數,那麼什麼情況下,按位拷貝會失效,默認的構造函數橫空出世呢 ,其實個人覺得挺像默認構造函數(default constructor)的,也有四種情況:

(這邊和default constructor差不多)

    default constructor 和copy constructor 有點像,裏邊的做的工作也是有一樣的地方,比如說虛函數表的初始化,

如果代碼是A a ,或者 A a = new A();在上述四種條件下,編譯器會合成default constructor

如果代碼是A a(b),或者A a= new A(b),或者A a = b,編譯器就會合成copy constructor


2.23第三個拷貝指定運算符

    copy assignment operator 拷貝指定運算符 這個和a=b,有點不一樣,A a=b,是因爲a還沒有值,用對象b來初始化,會出現合成copy constructor,如果 a已經有值了 就是a = b;

情況差不多也是那四種情況下,bitwise copy失效,那麼會調用 copy assignment operator


3.總結

    對象初始化的時候如果class的內容非常簡單隻有一些簡單類型變量,那麼無論怎使用一般都是bitwise copy即可完成賦值,如果出現上面說的四種情況:1.基類有相應構造函數,2.成員變量是某個類的對象,該類有相應構造函數。3.類中含有虛函數 4.繼承體系中有虛基類,這些情況,bitwise copy 會失效,default constructor ,copy constructor,copy assignment operator,都會被合成,在不同使用情況下,被調用,

A a;就調用default constructor,

A a = A(b),調copy constructor

A a = b;   調用 copy assignment operator


PS:以上的是個人看書《深度探索C++對象模型》時的感悟,不保證全是正確的,申明:我還是個菜鳥,如果有大神看到了,發現錯誤,歡迎指正,請輕拍



 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章