13 拷貝控制

2021-08-08 15:50:43 字數 3441 閱讀 5257

拷貝控制

一些特殊的情況使用拷貝初始化:

但是使用emplace建立的元素都進行直接初始化;

在函式呼叫的過程中,具有非引用型別的引數要執行拷貝初始化,當函式具有乙個非引用的返回值型別時,返回值用來初始化呼叫方的結果;

這樣來總結一下直接初始化和拷貝初始化:

當建構函式只有乙個形參時,就有一套預設的隱式轉換規則,然後關鍵字explict恰好可以抑制這種隱式轉換,並且隱式轉換只能夠轉換一步;

在進行拷貝初始化的過程中,編譯器可以但是不必跳過拷貝或者移動建構函式;

拷貝賦值運算子

析構函式

無論何時乙個物件被銷毀,就會自動呼叫其析構函式;

析構函式是自動執行的,不需要擔心何時釋放這些資源;

需要注意是,當指向乙個物件的引用或者指標離開作用域時,析構函式不會被執行;

析構函式也可以由編譯器自己進行生成,通常合成析構函式體為空,特殊情況例外;

加強理解:

三/五法則

基本原則之一: 如果乙個類需要乙個析構函式,那麼他肯定也需要乙個拷貝建構函式和乙個拷貝賦值運算子;

如果僅僅定義了析構函式,那麼在使用合成的拷貝建構函式以及賦值運算子函式時,可能會出現指標的淺拷貝現象,導致指標釋放出現錯誤;

在一些情況下,只需要拷貝或者賦值操作,並不一定需要析構函式,書裡面給出了一種特殊情況;

基本原則之二: 如果乙個類需要乙個拷貝建構函式,幾乎肯定他也需要乙個拷貝賦值運算子,反之亦然,但是並不意味著一定需要析構函式;

關於=default使用的兩種情況:

阻止錯誤的拷貝,不應該通過不定義控制拷貝成員,因為編譯器會自動生成;

c++11標準中可以通過拷貝建構函式以及賦值運算子定義為刪除函式來阻止拷貝,使用=delete,表示的含義是首先通過自己宣告拷貝建構函式以及賦值運算子防止編譯器自動生成,但是在宣告時,使用=delete表示這個函式被禁止使用;

=delete必須出現在函式第一次宣告時,編譯器需要在宣告時知道乙個函式是刪除的,以便於組織任何使用該函式的操作;

=default僅僅影響的是為這個成員生成的**,因此直到編譯器生成**時才需要;

我們幾乎可以對任何函式指定=delete,刪除函式的主要作用是用來禁止拷貝控制成員,但是也是可以用來引導函式匹配過程;

=default只能夠用於編譯器可以合成的預設建構函式或拷貝控制成員;

析構函式是不能夠定義為=delete的,如果存在這樣的定義,編譯器不允許定義該型別的變數或者建立該型別的臨時物件,雖然可以動態的分配這種型別的物件,但是不能夠釋放這些物件;

對於析構函式已經刪除的型別,不能夠定義該型別的變數或釋放指向該型別動態分配物件的指標;

一些可以刪除的拷貝控制成員函式(指的是編譯器自動生成的)

在新標準之前,類是通過將拷貝建構函式和拷貝賦值運算子宣告為private來阻止拷貝的;

乙個好的建議是:希望阻止拷貝的類應該使用=delete來定義他們自己的拷貝建構函式和拷貝賦值運算子,而不是將其宣告為private;

拷貝管理和資源管理

賦值運算子結合了析構函式和建構函式的特點,類似於析構函式,組織運算子會銷毀左側運算物件的資源,類似於拷貝建構函式,賦值運算子會從右側拷貝資料;

深拷貝例項:

hashptr& hasptr::operator=(const hasptr &rhs)

首先拷貝右側運算物件,然後處理自身賦值的情況,可以保證在異常發生時,**執行仍然是安全的;

賦值運算子注意:

行為更像指標的類

在上面**的基礎上面新增交換操作,交換操作本質上是指標的交換,而不是元素副本的交換;

一般情況下,應該使用類裡面本身的swap函式,如果類裡面已經有定義的swap函式,那麼就不應該使用標準庫提供的swap函式;

使用拷貝和交換的賦值運算子預設就是異常安全的,並且能夠出資自複製現象;

拷貝控制

動態記憶體管理

物件移動:

move函式: 雖然不能夠將乙個右值引用直接繫結到乙個左值上面,但是可以顯示的將乙個左值轉換為對應的右值引用型別,容納後通過move的標準庫函式來獲得繫結到左值上的右值引用;

移動建構函式以及移動賦值運算子

編譯器會自動生成移動建構函式的幾種情況:

總結上述規則:只有當乙個類沒有定義任何自己版本的拷貝控制成員,並且它的所有的資料成員都能移動構造或者移動賦值時,編譯器才會為它合成移動建構函式或者移動賦值運算子;

移動建構函式的幾大特點:

移動操作移動的是右值,拷貝操作拷貝的是左值;

如果沒有移動建構函式,但是定義了拷貝建構函式,函式匹配規則保證這類物件將會被拷貝,而不是被移動,即使嘗試進行移動,也會被拷貝;

使用拷貝建構函式或者拷貝賦值運算子來代替移動建構函式以及移動賦值運算子幾乎是安全的;

拷貝建構函式是不會改變原來物件的值;

如果乙個類有乙個可用的拷貝建構函式二沒有移動建構函式,則其物件是通過拷貝建構函式來移動的,拷貝賦值運算子和移動賦值運算子是類似的;

class hasptr 

//賦值運算子既是移動賦值運算子,也是拷貝賦值運算子;

hasptr& operator=(hasptr rhs)

};

比較特殊的是第二個實現了移動賦值運算子以及拷貝賦值運算子兩種功能,可以根據傳入的值是右值還是左值,來選擇進行拷貝賦值或者是移動賦值操作;

所有的五個拷貝控制成員應該是乙個整體,一般來說,如果乙個類定義了任何乙個拷貝操作,他就應該該定義所有的五個操作,同時在拷貝並非是必須要的情況下,還應該通過移動建構函式和一棟在賦值運算子來避免開銷;

移動迭代器介面卡,乙個移動迭代器介面卡通過改變給定迭代器的解引用運算子的行為來適配此迭代器,移動迭代器解引用運算子生成乙個右值引用,可以通過標準庫的make_move_iterator函式將乙個普通迭代器轉換為乙個移動迭代器,這個函式接受乙個迭代器引數,返回乙個移動迭代器;

新生成的移動迭代器支援正常的迭代器操作;

uninitialized_cpoy;

void strvec::reallocate()

移動迭代器和移動操作有著相同的特性,在移動操作之後源物件無法保證還是期望的值;

不可以隨意使用移動操作,在移動建構函式和移動賦值運算子這些類實現**之外的的地方,只有確定需要進行移動操作,並且移動操作是安全的,才可以使用std::move;

右值引用和成員函式

13章,拷貝控制

ex1 1,拷貝建構函式的定義 乙個建構函式的第乙個引數是自身型別的引用,且任何額外引數都有預設值。2,出現場合 ex2 拷貝建構函式自己的引數必須是引用型別。因為如果不是的話,在呼叫拷貝建構函式時,將乙個物件作為實參傳遞給乙個非引用型別的形參時,會再次呼叫該拷貝建構函式陷入死迴圈。ex5haspt...

第13章 拷貝控制

拷貝建構函式class foo sales data sales data const sales data orig bookno orig.bookno units sold orig.units sold revenue orig.revenue 拷貝賦值運算子foo operator con...

第13章 拷貝控制

拷貝構造的第乙個引數是自身類型別的引用,且任何額外引數都有預設值,成為拷貝建構函式。拷貝建構函式通常不應該是explicit,可以接受非const引用。class foo 合成拷貝建構函式 編譯器會自動合成拷貝建構函式。一般情況中,合成的拷貝建構函式會將其引數的成員逐個拷貝到正在建立的物件中。編譯器...