2016-01-06 39 views
2

我讀這篇文章:What are move semantics?移動語義如何保留臨時變量的數據?

注意,在該職位轉移構造函數給出的例子是:

string(string&& that) 
{ 
    data = that.data; 
    that.data = nullptr; 
} 

我感到十分困惑,當我們使用string a(x+y)構造一個新的字符串。由於x+y的結果是一個臨時變量,它很快就會被銷燬。這意味着複製指針(data = that.data)確實會在原始數據(應該存儲在函數調用完成後清理的棧幀x+y)被銷燬之後,通過懸掛指針進行復制。看來設置that.data爲nullptr將無濟於事,因爲無論如何堆棧幀都會被清理乾淨。

任何人都可以解釋爲什麼這不是一個問題?那麼C++如何處理這種情況呢?

+0

移動對象通常改變靶和源對象(目標竊取數據和離開什麼也沒有的源 - 說不定數據的交換) –

+0

'X + y'創建保存一個指針'data'對存儲器的臨時字符串在包含相關數據的堆上。如果臨時超出範圍,它在堆上擁有的內存將被刪除。但此舉構造你引述改變了一種方式,它不擁有該內存了臨時 - 所以它會走出去的範圍,而不實際接觸的內容。相反,新創建了''現在擁有該內存(其'data'點的數據,最初是由'X + y'擁有)。 – Pixelchemist

回答

2

由於x + y的結果是一個臨時變量,它很快就會被銷燬。這將意味着在指針(數據= that.data)複印的確會被複制在懸擺指針

都能跟得上。你複製指針,現在新的字符串有數據,然後你將臨時指針設置爲nullptr,這樣當臨時數據被銷燬時它不會刪除字符串數據。

你可以看到這是如何在這個小例子工作

#include <iostream> 

struct Foo 
{ 
    int * f; 
    Foo(int size) : f(new int[size]) 
    { 
     for (int i = 0; i < size; i++) 
      f[i] = i; 
    } 
    Foo() : f(nullptr) {} 
    ~Foo() { delete [] f; } 
}; 

int main() 
{ 
    int size = 10; 
    Foo b; // b is empty 
    { 
     Foo f(size); // now f has an of size 10 
     // if we now swap the contents like the move operation does 
     b.f = f.f; 
     f.f = nullptr; 
    } // f goes out of scope and ~Foo() is called 
    // now here b.f is valid as delete on nullptr did nothing 
    for (int i = 0; i < size; i++) 
      std::cout << b.f[i] << " "; 
} 

Live Example

0

string數據的情況下被保存在堆上,而不是堆棧幀。 「字符串」對象只包含指針並可能包含一些其他數據(例如長度)。因此,您顯示的移動構造函數有效地「盜取」其數據所有權的對象。第二行將nullptr賦值給臨時數據指針是必要的,以避免同一個臨時對象的析構函數刪除我們偷取的數據(因爲在nullptr上調用delete將保證不起作用)。

4

當你這樣做:

string a(x + y); 

它相當於:

string temp(x + y); 
string a(move(temp)); 
//destroy temp 

爲您引用的移動構造函數相關的代碼需要athisthattemp,所以它可能是內嵌爲:

string temp(x + y); 
string a(/*uninitialized*/); 
a.data = temp.data; 
temp.data = nullptr; 
//destroy temp 

正如你所看到的,temp.data是無效的,所以temp的析構函數變爲空操作,實際的數據在預期的a內仍然存在。

看起來你的困惑來自data的由來。在最簡單的string實施,string::data始終是一個內存動態分配的塊:

string(const char *str) 
{ 
    size_t len = strlen(str); 
    data = new char[len + 1]; 
    strcpy(data, len); 
} 
~string() 
{ 
    delete[] data; 
} 

即使string被分配在棧上,如tempa,甚至xy他們data內存塊動態。

真正的,真實世界的string實現通常做非動態的短串優化。但是,如果你這樣做,那麼移動構造函數(和其他成員函數)會更復雜一些。

+0

@OneZero:在評論中解釋太長。請看我更新的答案。 – rodrigo

+0

如果它不是一個'string',但只是有(而不是使用'new')[5]''int數據定製對象類型?在這種情況下,這是否意味着我們不能簡單地複製'data'指針? – OneZero

+0

@OneZero在這種情況下'data'不是指針但陣列,並且在移動的構造僅僅是沒有用的。 – rodrigo