1. 程式人生 > 其它 >C++備忘錄(3)移動建構函式

C++備忘錄(3)移動建構函式

技術標籤:C++

拷貝建構函式

當類成員變數裡面有指標時,為了預防淺拷貝,我們需要手動實現拷貝建構函式。
拷貝建構函式呼叫的場景:

  • 初始化新物件
  • 作為引數或是返回值

下面程式碼中均用g++編譯且帶引數 -fno-elide-constructors

class Test                            
{                                     
public:                               
    Test()                            
    {                                 
        cout <<
"Test create!" << endl; } ~Test() { cout << "Test dereate!" << endl; } Test(const Test& one) {
cout << "this is copy func" << endl; } Test& operator=(const Test& one) { cout << "this is operator = func" << endl; }
}; Test func1() { return Test(); } int func2(Test one) { return 0; } auto main()-> int { cout << "-------1----------" << endl; Test one; cout << "-------2----------" << endl; Test two = one; cout << "-------3----------" << endl; Test three = func1(); cout << "-------4----------" << endl; func2(three); cout << "-------over-------" << endl; return 0; }

結果:

-------1----------
Test create!
-------2----------
this is copy func
-------3----------
Test create!
this is copy func
Test dereate!
this is copy func
Test dereate!
-------4----------
this is copy func
Test dereate!
-------over-------
Test dereate!
Test dereate!
Test dereate!

func1呼叫時因為呼叫建構函式生成了一個區域性物件,函式返回時,區域性物件銷燬前複製到一個臨時物件,再從臨時物件複製到呼叫處的物件three,發生了兩次拷貝構造。

拷貝建構函式說白了就是複製一份物件,將源物件所有的資源全部複製一份,生成新的物件,多次呼叫很影響效率。

std::move()

這裡先說std::move(), 比較官方的說法是,它能將左值強制轉換位右值引用,而右值通俗點可以理解為”臨時的值“。所以std::move()可以將一個正常的變數變成臨時變數。所以,一臉懵逼,move到底要做啥?

{                            
    string one   = "hello";  
    string two   = move(one);
                             
    cout << "one:" << one << endl;  //結果位空
    cout << "two:" << two << endl;  //結果“hello"                                                                                                                                                                   
    return 0;                
} 

上例程式碼中,move”掏空“了 one, 或者說move將物件one的資源轉移到了物件two。記得不要使用被move過的物件
所以,std::move() 存在的意義就是減少不必要的拷貝。因為它能將”資源“直接轉移而不是複製。

所以回到上面的問題,我們希望能用move去掉上面的臨時物件,於是我們將上面的func1()改為:

Test func1()                                                                                                                                                                                                 
{            
    return move(Test());      
}            
                
auto main()-> int 
{                  
    Test one = move(func1());
    return 0;
}            

執行結果仍然為

Test create!
this is copy func
Test dereate!
this is copy func
Test dereate!
Test dereate!

很明顯我們的手動嘗試更改拷貝建構函式(注意,此時我們沒有寫類的移動建構函式)沒有起作用,或者說std::move()似乎沒有被正確使用。那麼move到底應用場景是什麼?應該怎麼用?
一個能使用move的物件,其應該滿足的條件是:

  • 該物件使用了資源並定義了移動建構函式移動賦值運算子

值得說明的是,我們可以通過std::move()來轉移智慧指標unique_ptr的所有權

unique_ptr<int> up1(new int(11));
unique_ptr<int> up2 = move(up1);  

移動建構函式

給上面的Test類新增兩個介面:

//新增的移動建構函式
Test(const Test&& one) noexcept
{     
	cout << "this is TEST &&  move ctor func" << endl;
} 

//新增的移動賦值函式
Test& operator=(const Test&& one) noexcept
{     
	cout << "this is TEST &&  move operator= func" << endl;
}     

移動建構函式就是相當於使用了move(),呼叫移動構造比呼叫拷貝建構函式的速度更快,更能節省時間。

拷貝建構函式是對整個物件的拷貝,而移動建構函式是直接將源物件的記憶體資源轉移到新物件,省去了資料的拷貝時間。

所以從效率上考慮,我們應該儘量用移動建構函式而非拷貝建構函式。那麼如何保證我們使用的是移動建構函式,或是說在什麼樣的情況下會呼叫移動建構函式?

使用臨時物件初始化當前類的物件,編譯器會優先呼叫移動建構函式來完成此操作。
只有當類中沒有移動建構函式時,編譯器才會呼叫拷貝建構函式。

換句話說,只要你定義了移動建構函式,就是編譯器會在之前呼叫拷貝建構函式的地方換成移動建構函式。挺好,省事。