第十三章、拷贝控制
一、拷貝控制操作
1、拷貝構造函數:一個構造函數的一個參數是自身類類型的引用,且額外參數都有默認值
class Foo{ public:Foo(const Foo&); //拷貝構造函數;;最好是const類型;不應該是explicit的 }- 拷貝構造函數通常會被隱式使用,所以不應該是explicit
- 如果我們沒有為一個類定義拷貝構造函數,編譯器會定義一個合成的拷貝構造函數
- 合成的拷貝構造函數會將其非static成員簡單的拷貝到正在創建的對象中
- 拷貝初始化發生的情況
- 用=定義變量時
- 將一個對象作為實參傳遞給一個非引用類型的參數
- 拷貝構造函數用來初始化非引用類類型參數,所以拷貝構造函數定義必須是引用類型
- 從一個返回類型為非引用類型的函數返回一個對象
- 用花括號列表初始化一個數組中的元素或一個聚合類中的成員
2、拷貝賦值運算符
- 重載運算符本質上是函數,由operator關鍵字后接表示要定義的運算符的符號組成。
- 運算符函數也要有一個返回類型和一個參數列表
- 賦值運算符通常返回一個指向其左側運算對象的引用 Foo& operator = (const Foo&);
3、移動構造函數
4、移動賦值運算符
5、析構函數:釋放對象使用的資源,并銷毀對象的非static數據成員
~Foo();- 沒有返回值,不接受參數
- 按成員初始化順序的逆序銷毀
- 調用析構函數的情況:
- 變量離開作用域時被銷毀
- 一個對象被銷毀時,其成員被銷毀
- 容器被銷毀時,其元素被銷毀
- 臨時變量,當創建它的完整表達式結束時被銷毀
- 當指向一個對象的引用或指針離開作用域時,析構函數不會執行
- 析構函數函數體本身不直接銷毀成員;在之后的隱含的析構階段中被銷毀
6、三/五法則
- 當我們決定一個類是否需要定義自己版本的拷貝控制成員時,基本原則是首先確定這個類是否需要一個析構函數
- 合成析構函數不會delete一個指針數據成員;這個時候需要自己定義一個析構函數來釋放構造函數分配的內存
- 如果一個類需要自己定義的析構函數,則也需要自己定義的拷貝賦值運算符和拷貝構造函數
- 需要拷貝構造函數也需要拷貝賦值運算;反之也是;但是不一定需要自己的析構函數
7、在函數的參數列表后加上=delete指出該函數為刪除的
- 析構函數不能是刪除的:如果是刪除的,則無法銷毀此類型的對象了
- 一個類有const成員,則它不能使用合成的拷貝賦值運算符:因為不能被賦值
二、拷貝控制和資源管理
1、通常管理類外資源的類必須定義拷貝控制成員;這種類需要通過析構函數來釋放對象所分配的資源
2、當編寫一個復制運算符時,一個好的模式是先將右側運算對象拷貝到一個局部臨時對象中。當拷貝完成后銷毀左側運算對象的現有成員就是安全的:防止自己賦值給自己
HasPtr& HasPtr::operator=(const HasPtr &rhs) {auto newp = new string(*rhs.ps);delete ps;ps = newp;i = rhs.i;return *this; }三、對象移動
1、標準庫容器、string和shared_ptr類既支持移動也支持拷貝。IO類和unique_ptr類可以移動但不能拷貝
2、右值引用:必須綁定到右值的引用;通過&&來獲得右值引用
- 右值引用只能綁定到一個將要被銷毀的對象
- 可以綁定到要求轉換的表達式、字面常量或是返回右值的表達式,但不能直接綁定到一個左值上
- 返回非引用類型的函數、連同算術、關系、位以及后置遞增/減運算符都可以生成右值。可以把一個const的左值引用或一個右值引用綁定到這類表達式上
- 返回左值的引用的函數,連同賦值、下標、解引用和前置遞增/遞減運算符,都是左值的表達式,可以把一個左值引用綁定到這類表達式上
- 左值有持久的狀態,右值要么是字面常量,要么是在表達式求值過程中創建的臨時對象
- 右值所引用的對象將要被銷毀
- 該對象沒有其他用戶
- 變量是左值,因此不能將一個右值引用直接綁定到一個變量上,即使這個變量時右值引用類型也不行
- 調用move的標準庫函數來獲得一個綁定到左值上的右值引用
- 調用move就意味著除了對rr1賦值或銷毀它外,將不能再使用它
- 使用move的代碼應該使用std::move而不是move,這樣可以避免潛在的名字沖突
3、移動構造函數和移動賦值運算符
StrVec::StrVec(StrVec &&s) noexcept //第一個參數為類類型的引用;其他參數都必須有默認參數:elements(s.elements), first_free(s.first_free) //使用noexcept來提示編譯器不拋出異常 {s.elements = s.first_free = nullptr; //確保移后源對象主語銷毀無害的狀態 } 如果一個類定義了自己的拷貝構造函數、拷貝賦值運算符或者析構函數,編譯器就不會為它合成移動構造函數和移動賦值函數了; 只有當一個類沒有定義任何自己版本的拷貝控制成員,且它的所有數據成員都能移動構造或移動賦值時,編譯器才會為它合成移動構造函數或移動賦值運算符 定義了一個移動構造函數或移動賦值運算符的類必須也定義自己的拷貝操作,否則這些成員默認被定義為刪除的 當一個類沒有移動構造函數時,函數匹配規則保證該類型的對象會被拷貝,即使我們試圖調用move來移動它們也是如此4、右值引用和成員函數
- 區分移動和拷貝的重載函數通常一個版本接收const T&;另一個版本接收&&;其他的const T&&和 T&不需要
5、在參數列表后面可以放置一個引用限定符
- 對于&限定的函數,只能將它用作左值;對于&&限定的函數,只能用作右值
- 一個函數可以同時使用const和引用限定,但是引用限定符必須跟在const限定符后面
- 編譯器會根據對象的左/右值屬性來確定使用哪個版本的函數
- 如果一個成員函數有引用限定符,則具有相同參數列表的所有版本都必須有引用限定符
轉載于:https://www.cnblogs.com/dylqt/p/4880926.html
總結
- 上一篇: foc学习笔记2——svpwm
- 下一篇: 全国计算机等级考试题库二级C操作题100