本文由@呆代待殆原創,轉載請註明出處。程序員
對於一個類來講,咱們把copy constructor、copy-assignment operator、move constructor、move-assignment operator、destructor統稱爲copy control。數組
今天咱們先來聊聊其中的copy constructor、copy-assignment operator的destructor這三個。函數
copy constructor優化
copy constructor:一個constructor若是他的第一個參數是對類的引用,且其餘的參數都有缺省值(default values)則,這是一個copy constructor。spa
1,第一個參數必須是引用類型,由於當咱們把一個object當作參數傳遞給一個方法的非引用變量的時候會自動調用copy constructor方法,若是copy constructor自身的參數就是非引用類型的話,這個方法就會引發無限遞歸調用,而後你的程序就boomshakalaka~~。指針
2,通常咱們會把第一個參數設成const,由於通常狀況下不會對其進行修改,除非你另有打算。code
3,由於copy constructor在不少狀況下是默認調用的,如如下狀況,因此通常不會把copy constructor設成explicit。blog
1 std::string s; 2 std::string s1 = s; //隱式調用了copy constructor 3 std::string s2 = std::string(s1);//顯式調用了copy constructor
1 class Foo{ 2 public: 3 Foo(const Foo&); 4 //... 5 };
什麼時候發生copy constructor調用遞歸
爲了弄清這個問題咱們須要弄清另一組概念:direct initialization 和copy initialization。內存
direct initialization:要求編譯器按照通常的方法匹配(function matching)來選擇要調用的方法。
copy initialization:要求編譯器將右操做數拷貝到左操做數,有必要的話還會進行類型轉換,這個過程會調用copy constructor或者move constructor(本文暫不介紹)。
1 std::string s1("balabala"); //direct initialization 2 std::string s2(10, 'a'); //direct initialization 3 std::string s3 = s2; //copy initialization 4 std::string s4 = std::string(s3); //copy initialization 5 std::string s5 = "const char* converts to string";//copy initialization
copy initialization發生的狀況以下:
1,用=來初始化定義的變量時。
2,把一個object當作參數傳遞給一個方法的非引用變量的時。
3,方法返回一個非引用類型的object時。(返回時會首先生成一個臨時object)
4,用花括號列表初始化一個數組或聚合類(aggregate class)成員時。
因爲編譯器帶來的誤解:
如今的編譯器有時候會自動繞過copy constructor即編譯器會把下面這一句話
std::Book book = "9-9-9-9";//假設Book是一個自定義的類
換成下面這個
std::Book book("9-9-9-9");
請注意在執行上上面兩句語言是徹底不同的,第一句會首先調用Book(const char*)構造函數生成一個臨時object而後再調用Book(const Book&)把臨時object複製給book。而第二句話會直接調用Book(const char*)而後完事兒。若是你想驗證他們的區別能夠實現Book類並將Book(const Book&)設置成私有方法(防止編譯器自動優化),以後你就會發現第一條語句沒法執行了。
Copy-assignment operator
copy-assignment operator:寫這個方法就是對=操做符進行重載。
1,copy-assignment operator的返回值通常是對其左操做數(left-hand operand)的引用,這是爲了讓object的行爲更像內置類型而決定的。
1 class Foo{ 2 public: 3 Foo& operator=(const Foo&); 4 //... 5 };
什麼時候發生copy-assignment operator調用
答案很顯然是用到=操做符的時候啊,可是這裏要注意的是
初始化的時候並不會調用copy-assignment operator
初始化的時候並不會調用copy-assignment operator
初始化的時候並不會調用copy-assignment operator
重要的事情說三遍,舉例以下
1 std::string s; 2 std::string s1 = s; //對s1進行初始化,調用的是copy constructor 3 s1 = s; //對s1進行賦值,調用的是copy-assignment operator
Destructor
Destructor:destructor有兩個部分,function body和destruction part,前者由類的編寫者寫明須要作的內容,後者是隱式的,不須要程序員關心,在function body執行完後自動執行,會銷燬類的非靜態數據成員。
1,由於Destructor沒有參數,因此它是不能被重載的
1 class Foo{ 2 public: 3 ~Foo(); 4 //... 5 };
什麼時候發生Destructor調用
1,當超出object 的做用域(scope)時。
2,容器銷燬時(container),裏面的元素(element)也會跟着調用自身的destructor從而銷燬。
3,人爲使用delete的時候。
4,由某個表達式建立的臨時變量在這個表達式執行完後將自動調用destructor從而銷燬。
5,類的成員若是自身有destructor,會在這個類銷燬的時候調用自身的destructor。
關於編譯器自動提供的版本(Synthesized)
Synthesized copy constructor:即便咱們提供了其餘版本的copy constructor,編譯器仍然會提供這個版本的copy constructor給咱們,它會依次複製非靜態成員給被建立的object,對數組也能正常工做,對於class類型會調用它們本身的copy constructor。
Synthesized copy-assignment operator:行爲和Synthesized copy constructor相似,依次把非靜態成員複製給左操做數。
Synthesized destructor:destructor的function body爲空。
關於什麼時候咱們須要自定義上述的三個方法
1,當須要destructor時,上述三給方法都是須要的。
2,當須要copy constructor時,copy-assignment operator也是須要的,反之亦然。
而當咱們須要刪除本身動態分配的內存時,就要用到destructor。
當咱們須要進行深度複製時會用到另外兩個,好比對指針指向的元素進行復制等等。
關於delete和default的用法
咱們能夠用default顯示聲明咱們想要用默認版本的copy control,也能夠用delete顯示聲明咱們徹底不須要這類方法來達到禁止這個object進行相關的複製和賦值操做。
1,咱們能delete除了destructor之外的全部方法來達到顯示告知這個object不能進行相關操做的目的,delete只能寫在一次聲明出現的地方。
2,咱們能對全部有默認版本的函數用default顯示聲明咱們須要這個默認版本,default能夠寫在方法聲明的地方也能夠寫在方法定義的地方。
1 class Foo{ 2 public: 3 Foo() = default; //顯式說明使用默認版本 4 Foo(const Foo&) = delete; //delete copy constructor 5 Foo& operator=(const Foo&) = delete; //delete copy-assignment operator 6 ~Foo() = default; //顯式說明使用默認版本 7 void myFuntion() = delete; //delete本身的方法 8 //... 9 };
參考資料:《C++ primer 英文第五版》