當定義一個新類型的時候,須要顯式或隱式地指定複製、賦值和撤銷該類型的對象時會發生什麼——這是經過定義特殊成員:複製構造函數、賦值操做符和析構函數來達到的。若是沒有顯式定義複製構造函數或者賦值操做符,編譯器(一般)會爲咱們定義。 函數
看看編譯器默認的構造函數和賦值操做符作了些什麼工做: 優化
class_a.h this
#ifndef _CLASS_A_H #define _CLASS_A_H class A { public: A(); A(int a); private: int a; }; #endif
class_a.cpp spa
#include "class_a.h" A::A(): a(0) { } A::A(int a) { this->a = a; }
main.cpp 操作系統
#include "class_a.h" int main() { A obj1; //調用無形參構造函數 A obj2(2); //形參爲int的構造函數 A obj3(obj1); //默認複製構造函數 A obj4 = 3; //調用形參爲int的構造函數產生類A的臨時變量,調用默認賦值操做符進行賦值 A obj5 = obj1; //調用默認賦值操做符 return 0; }
編譯執行g++ -g main.cpp class_a.cpp
反彙編objdump -S a.out
可知:
默認複製構造函數執行的實質將obj1的內存複製到obj3的內存上
默認複製操做符當形參爲非A&時,調用相應形參的構造函數(編譯器層面上並無生成臨時變量的內存,應該是編譯器優化掉了;生成臨時變量是C++語法層面上的);不然直接將對象的內存複製到新對象上 指針
原來默認的複製構造函數以及默認的賦值操做符只是複製sizeof(A)大小的內存複製給新的對象,或者是調用了相應的單形參的的構造函數;這也就是咱們常說的淺拷貝,當咱們的類中有指針或者引用時,必定要顯式的定義複製構造函數以及賦值操做符。 code
咱們顯式的定義了複製構造函數以及默認賦值操做符: 對象
class_a.h 進程
#ifndef _CLASS_A_H #define _CLASS_A_H class A { public: A(); A(int a); A(const A& obj_a); A& operator=(const A& obj_a); ~A(); private: int a; }; #endif
class_a.cpp 內存
#include "class_a.h" A::A(): a(0) { } A::A(int a) { this->a = a; } A::A(const A& obj_a) { this->a = obj_a.a; } A& A::operator=(const A& obj_a) { this->a = obj_a.a; return *this; } A::~A() { }
main.cpp
#include "class_a.h" int main() { A obj1; //調用無形參構造函數 A obj2(2); //調用形參爲int的構造函數 A obj3(obj1); //調用複製構造函數 A obj4 = 3; //調用形參爲int的構造函數產生臨時變量(若設爲explicit編譯不過) //調用複製操做符進行賦值 A obj5 = A(4); //調用形參爲int的構造函數產生臨時變量(若設爲explicit編譯照樣經過) //調用複製操做符進行賦值 A obj6 = obj1; //調用複製構造函數產生臨時變量,調用複製操做符進行賦值 A obj7 = A(obj1); //調用複製構造函數產生臨時變量,調用複製操做符進行賦值 return 0; }
編譯 g++ -g main.cpp class_a.cpp
反彙編objdump -S a.out
可知:
obj4並無產生臨時變量,生成過程跟obj2同樣,調用形參爲int的構造函數
obj5也並無產生臨時變量,生成過程跟obj2同樣,調用形參爲int的構造函數
obj6也沒有產生臨時變量,直接調用自定義複製構造函數
obj7也沒有產生臨時變量,也是直接調用自定義複製構造函數
看來產生臨時變量是C++語法上的,在編譯器上進行了優化,不然產生臨時變量的效率比較低,先要調用構造函數,而後還要調用析構函數進行析構,並且上述代碼也只是調用了7次構造函數,驗證了在編譯器上沒有產生臨時變量。
C++ primer說:由於用於向函數產地對象和從函數返回對象,該構造函數通常不該該設置爲explicit。
將上述代碼的複製構造函數設置成explicit,編譯報錯:obj4, obj5, obj7 :no matching function for call to ‘A::A(A)’obj6: no matching function for call to ‘A::A(&A)’,能夠猜想C++語法上的臨時變量是經過A::A(A)和A::A(A&)產生的,由於沒有定義A::A(A)的構造函數,而A::A(A&)被定義成了explicit抑制了臨時變量的產生。
複製構造函數以及賦值操做符的自定義,能夠解決淺拷貝的問題;不會致使因爲對象的析構中釋放new對象致使其餘對象的影響。
可是咱們仍是得注意另外一個問題,當對象中帶有操做系統進程級別的一些參數時(好比fd),在函數調用的時候不能在函數內部定義一個臨時變量,並將對象賦值給這個臨時變量,由於臨時變量析構時就會調用自定義的close(fd),這樣就會致使意想不到的結果,這種狀況下,咱們須要定義一個該對象的指針而不是一個臨時變量對象。