2014-07-22 112 views
1

當我有一個類,我試圖返回它的實例調用,但調用析構函數之前,我可以恢復它,這和當它確實掉出的範圍在後面的功能是析構函數被再次調用並導致中斷。我編輯它來修復錯誤,但我想知道是否應該調用它的析構函數。析構函數返回值

CBuffer BufferReader::read(const int size){ 
    const auto raw = read_raw(); 
    skip(size); 
    return CBuffer(raw, size, true); 

    // Dstructor is called in this example 

    CBuffer out(read_raw(), size, true); 
    skip(size); 
    return std::move(out); 
} 
+0

理論上返回使得副本返回,所以原理在理論上被破壞。給定一個正確定義的類,這不是一個問題。 – chris

+2

除非[RVO](http://en.wikipedia.org/wiki/Return_value_optimization)進來,它應該在複製/移動後調用析構函數。 –

+0

爲什麼不展示CBuffer類的基礎知識?不是每個函數,而是這裏使用的構造函數,複製構造函數,析構函數和關鍵數據成員?您可能會得到有關如何改進複製構造函數以實現此功能的有用建議。 – NicholasM

回答

3

您正在返回CBuffer的副本。在函數內部創建的原始CBuffer在函數退出時被銷燬 - 至少在邏輯上是這樣。通常情況下,編譯器會將該副本作爲優化來使用,除非該優化被禁用(例如,通過進行調試構建)。

使用std::move並沒有真正改變這一點。通過從最初的CBuffer中竊取膽量,可以更有效地完成創建的副本。原始的CBuffer仍然必須銷燬,它可能不包含任何真實的數據。移動構造函數總是需要將原始對象保留在有效狀態,以便可以安全地銷燬它。

例如:

struct CBuffer { 
    char* data; 

    CBuffer(CBuffer&& original) 
     : data(original.data) 
    { 
     original.data = nullptr; 
    } 

    ~CBuffer() { delete [] data; } // data may be null, but that is fine 
            // delete will not do anything in that case. 
}; 

如果你可以使用std::vector代替,這是更好,因爲它處理這些細節給你。

+0

當我用std :: move返回它時,析構函數仍然被調用,編輯顯示std :: move代碼,根據我的理解轉換成r值並且不應該執行復制。 –

+0

@JoshuaWaring:發生移動時,它只是一種優化的複製方式。它不會改變是否調用析構函數。 –

+0

CBuffer只包含一個int大小和一個void *數據,如果移動被調用,那麼數據會發生什麼,它會變成一個nullptr?因爲CBuffers析構函數試圖刪除數據。 –

0

std::move不會任何東西的對象,它所做的就是通過引用轉發它的參數,並使它像一個右值綁定。換句話說,它只是返回一個匿名右值引用的對象,認爲:

template<class T> 
T&& std::move(T&& arg) 
{ 
    return static_cast<T&&>(arg); 
} 

(這是比這稍微複雜一些,但我的目標是爲了便於閱讀)

什麼實際發生在這裏出口是:

CBuffer rv(std::move(out)); 
return rv; 

這是調用CBuffer的移動構造函數。

很多人陷入思維std :: move的陷阱實際上執行的舉動,恕我直言,它的名字很差。

如果你寫的代碼是這樣的:

CBuffer BufferReader::read(const int size) 
{ 
    CBuffer out(read_raw(), size, true); 
    skip(size); 
    return out; 
} 

那麼大多數編譯器應該能夠執行「返回值優化」;也就是說,他們會悄悄假裝你做了等同於:

void BufferReader::read(const int size, CBuffer* out) 
{ 
    new (out) CBuffer(read_raw(), size, true); //in-place new 
    skip(size); 
} 

,並在您有

CBuffer x = reader.read(size); 

調用網站,他們會假裝你正在寫

CBuffer* x = (CBuffer*)alloca(sizeof(CBuffer)); 
reader.read(size, &x); 

再次簡化一下,上面的代碼是在棧上分配一個CBuffer,然後在被調用者內部初始化它。這節省了在回報中執行副本。