2013-03-20 78 views
3

我在尋找理解如何在C++中實現vector。有一個previous question問這個,所以我看了一下,我有一個小問題。假設在鏈接的問題的實施是正確的,讓我們來看看下面的代碼:C++:向量實現和動態內存分配

int main(){ 
    Vector<int> test2 = test_Vector(); 
    cout << test2[0] << endl; 
    return 0; 
} 


// below is NOT the STL vector object, but the one in the linked question, 
// in which the asker tries to implement STL vector himself/herself 
Vector<int> test_Vector(){ 
    Vector<int> test; 
    test.push_back(5); 
    return test; 
} 

據我瞭解,在testVector對象是在本地創建的,所以當test_Vector方法返回,本地對象超出範圍,從而調用析構函數和delete-動態數組。由於代碼實際工作和5打印,我想我錯了。什麼是正確的解釋?

+2

請參閱:[Return Value Optimization(RVO)](http://en.wikipedia。org/wiki/Return_value_optimization) – 2013-03-20 23:23:26

+0

有關RVO的觀點非常重要:通常程序員對於C++來說是新手,因爲擔心昂貴的副本而找不到有價值的對象。很多時候,根本沒有複製。 – juanchopanza 2013-03-20 23:25:44

回答

3

你是對的,但你錯過了一件重要的事情。

因爲您要退回Vector<int>,您應該將其視爲正在複製。這通常會調用複製構造函數,該構造函數將test複製到Vector<int>的新實例中。拷貝構造函數在鏈接的問題實現爲:

template<class T> 
Vector<T>::Vector(const Vector<T> & v) 
{ 
    my_size = v.my_size; 
    my_capacity = v.my_capacity; 
    buffer = new T[my_size]; 
    for (int i = 0; i < my_size; i++) 
     buffer[i] = v.buffer[i]; 
} 

注意,拷貝構造函數可能不被調用,由於返回值優化(見下面的評論吹毛求疵)。在許多情況下,編譯器可以優化拷貝,而C++標準允許這種優化可以改變程序行爲

無論是複製對象還是應用RVO,您都應該得到同樣的結果。只要你遵循正常的面向對象的做法,優化不應該毀了你的對象。

不管類型如何,你都應該想到函數返回值被傳遞的值(,即),然後考慮你的編譯器可能在做RVO。重要的是不要忘記Rule of Three(或四,或五)。

+1

當然,編譯器可以通過RVO優化臨時對象,所以如果在複製構造函數中有邏輯,它可能會被跳過。 :) – Joe 2013-03-20 23:15:50

+1

請將「它將調用複製構造函數」更改爲「它可能會調用複製構造函數」 – Slava 2013-03-20 23:16:45

+0

好吧,我想這可以解釋它,但請原諒我,我仍然是一個初學者;爲什麼在方法返回時調用複製構造函數?那是我必須知道的嗎? – naxchange 2013-03-20 23:17:20

1

他提供了一個公共的複製構造函數(技術上,他沒有使複製構造函數爲私有),所以標準C++邏輯開始並使對象的副本返回。新對象位於main的本地。在複製構造函數中,新的內存是malloc ed,並將數據複製過來。如果他沒有提供拷貝構造函數,它將訪問無效內存(試圖從已釋放指針訪問內存)

+1

在現實生活中,極有可能通過返回值優化消除副本。 – juanchopanza 2013-03-20 23:21:44

+1

@juanchopanza true但這不是您應該考慮的方式,因爲這不是一種優化功能,而且如果複製構造函數的格式不正確,則會根據優化情況獲得不同的行爲。 – Dave 2013-03-20 23:26:59

+1

當然,但硬幣的另一方面是,你也必須期望有可能(在上面的情況下,肯定是)沒有對拷貝構造函數的調用,因爲這是一個允許改變可觀察值的優化程序的行爲。 – juanchopanza 2013-03-20 23:28:46

1

test被返回時,它的拷貝構造函數被調用(理論上),它分配一個新的內存塊並複製test的內容。 test_Vector上的原始test被破壞(理論上)並且test2被賦值爲副本,這意味着賦值運算符將被調用(理論上),它將再次分配內存並從返回時創建的臨時對象複製數據。然後從test_Vector返回的臨時對象的析構函數被稱爲(理論上)。

正如你所看到的,沒有編譯器優化,這就是地獄:)如果你不做任何過於巴洛克式的操作並且編譯器很聰明,而且你的情況很簡單,那麼「理論上的」可以被忽略。查看this,瞭解C++ 11的更新情況。