右值引用
右值一般指的是無法用&取到存儲地址的。比如常量10
那麼我們可以使用&&來獲得右值
int &&j=10;
int e=10;
int &&j=e;//錯誤,因爲e是左值
移動構造函數
#include <iostream>
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()
{
if(num != nullptr)
{
delete num;
num = nullptr;
}
cout << "class destruct!" << endl;
}
private:
int *num;
};
demo get_demo()
{
return demo();
}
int main()
{
demo a = get_demo();
return 0;
}
在這個過程中,如果沒有經過返回值優化的話,會在get_demo函數return時返回一個臨時變量,該臨時變量會調用拷貝構造函數從return後面創建的demo對象中得到值,同時demo對象本身銷燬;然後demo會再次調用拷貝構造函數從臨時對象處賦值,同時也銷燬臨時對象。這樣其實一共調用了一次構造函數(return demo()),兩次拷貝構造函數(函數內創建的demo對象賦值給臨時變量,臨時變量賦值給demo),兩次析構函數(臨時變量和函數內創建的A對象),每次構造和析構的過程都要new和delete資源,這對於計算來說是極大的浪費。
過程中生成的都是匿名對象,無法通過&獲取地址,因此它是一個右值,可以通過引入移動構造函數來優化程序。
可以看到兩次深拷貝的過程都是通過移動構造函數來完成的,在移動構造函數中,參數是一個demo的右值引用,並且直接將新生成的對象的指針成員指向匿名對象所申請的堆空間,將匿名對象的成員指針置空,這樣就防止每次調用拷貝構造函數向堆上申請新的內存空間了,大大提高了效率。
所謂移動語義,指的就是以移動而非深拷貝的方式初始化含有指針成員的類對象。簡單的理解,移動語義指的就是將其他對象(通常是臨時對象)擁有的內存資源“移爲已用”。
move
默認情況下,左值初始化同類對象只能通過拷貝構造函數完成,如果想調用移動構造函數,則必須使用右值進行初始化。C++11 標準中爲了滿足用戶使用左值初始化同類對象時也通過移動構造函數完成的需求,新引入了 std::move() 函數,它可以將左值強制轉換成對應的右值,由此便可以使用移動構造函數。
#include <iostream>
using namespace std;
class MoveDemo
{
public:
MoveDemo() :num(new int(0))
{
cout << "construct!" << endl;
}
MoveDemo(const MoveDemo &d) :num(new int(*d.num))
{
cout << "copy construct!" << endl;
}
//添加移動構造函數
MoveDemo(MoveDemo &&d) :num(d.num)
{
d.num = nullptr;
cout << "move construct!" << endl;
}
~MoveDemo()
{
if (num != nullptr)
{
delete num;
num = nullptr;
}
cout << "class destruct!" << endl;
}
public:
int *num;
};
MoveDemo get_demo()
{
return MoveDemo();
}
int main()
{
MoveDemo demo;
cout << "demo2:\n";
MoveDemo demo2 = demo;
//cout << *(demo2.num) << endl; //可以執行
cout << "demo3:\n";
MoveDemo demo3 = std::move(demo);
//此時 demo.num = NULL,因此下面代碼會報運行時錯誤
//cout << *(demo.num) << endl;
return 0;
}
參考
https://blog.csdn.net/bureau123/article/details/112696446
https://www.cnblogs.com/wickedpriest/p/12662746.html