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++對象模型》時的感悟,不保證全是正確的,申明:我還是個菜鳥,如果有大神看到了,發現錯誤,歡迎指正,請輕拍