移動構造
所謂移動語義,指的就是以移動而非深拷貝的方式初始化含有指針成員的類對象。簡單的理解,移動語義指的就是將其他對象(通常是臨時對象)擁有的內存資源“移為已用”。
- 就是在設計類的時候,除了定義拷貝構造函數,再定義一個移動構造函數,這個移動構造函數因為形參使用右值引用的方式,所以只能接受右值(主要是匿名對象、臨時變量這種將亡值)對其使用淺拷貝,因此大大減小性能的開銷。
舉例實現
- 事實上,對于程序執行過程中產生的臨時對象,往往只用于傳遞數據(沒有其它的用處),并且會很快會被銷毀。因此在使用臨時對象初始化新對象時,我們可以將其包含的指針成員指向的內存資源直接移給新對象所有,無需再新拷貝一份,這大大提高了初始化的執行效率。
using namespace std;
class demo{
public:
demo():num(new int(0)){
cout<<"construct!"<<endl;
}
demo(const demo &d):num(new int(*d.num)){
cout<<"copy construct!"<<endl;
}
//添加移動構造函數
demo(demo &&d):num(d.num){
d.num = NULL;
cout<<"move construct!"<<endl;
}
~demo(){
cout<<"class destruct!"<<endl;
}
private:
int *num;
};
demo get_demo(){
return demo();
}
int main(){
demo a = get_demo();
return 0;
}
可以看到,在之前 demo 類的基礎上,我們又手動為其添加了一個構造函數。和其它構造函數不同,此構造函數使用右值引用形式的參數,又稱為移動構造函數。并且在此構造函數中,num 指針變量采用的是淺拷貝的復制方式,同時在函數內部重置了 d.num,有效避免了“同一塊對空間被釋放多次”情況的發生。
右值
- 程序執行結果中產生的
臨時對象(例如函數返回值、lambda 表達式等)既無名稱也無法獲取其存儲地址,所以屬于右值
。當類中同時包含拷貝構造函數和移動構造函數時,如果使用臨時對象初始化當前類的對象,編譯器會優先調用移動構造函數來完成此操作。只有當類中沒有合適的移動構造函數時,編譯器才會退而求其次,調用拷貝構造函數。
在實際開發中,通常在類中定義移動構造函數的同時,也會定義一個拷貝構造函數,
用右值初始化類對象時,會調用移動構造函數;
用左值初始化類對象時,會調用拷貝構造函數。
另外:默認情況下,左值初始化同類對象只能通過拷貝構造函數完成,如果想調用移動構造函數,則必須使用右值進行初始化。C++11 標準中為了滿足用戶使用左值初始化同類對象時也通過移動構造函數完成的需求,新引入了 std::move() 函數,它可以將左值強制轉換成對應的右值,由此便可以使用移動構造函數
。