2017-06-04 129 views
0
#include <iostream> 
#include <vector> 

struct T{ 
    T(){ 
     std::cout << "Constructor\n"; 
    } 
    ~T(){ 
     std::cout << "Destructor\n"; 
    } 
}; 

int main() { 
    std::vector<T> vec; 
    vec.push_back(T()); 
    vec.push_back(T()); 

    return 0; 
} 

輸出是:爲什麼emplace_back很重要?

(1)Constructor 
(2)Destructor 
(3)Constructor 
(4)Destructor 
(5)Destructor 
(6)Destructor 
(7)Destructor 

爲什麼有這麼多的desructors電話?我看到:

(1)consruct臨時對象temp1中

(2)自毀temp1中

(3)consruct臨時對象TEMP2

(4)自毀TEMP2

然後,它被稱爲temp1和temp2的拷貝構造函數或移動構造函數。所以,(5)和(6)是清楚的。但是(7)呢?

+9

另外還有一個拷貝構造函數;這可能證明照亮。另外,這將有助於在「push_back」調用之前,之後和之間打印某些內容 - 這樣,您就可以知道何時發生這些構造函數和析構函數的響應時間。 –

+3

您是否打算一次調用emplace_back並調用一次push_back?沒有看到兩次調用push_back的點,它與您的標題不匹配。 –

+0

當矢量需要增長其存儲空間時,它會分配新的(更大的)內存塊,將原始元素複製或移動到新的存儲空間,然後銷燬原始內存並釋放舊內存。額外的析構函數調用可能來自於此。 –

回答

3

讓我們擴展您的結構位:

struct T { 
    T() { 
     std::cout << "Constructor\n"; 
    } 

    T(const T&) { 
     std::cout << "Copy Constructor\n"; 
    } 

    T(T&&) { 
     std::cout << "Move Constructor\n"; 
    } 

    ~T() { 
     std::cout << "Destructor\n"; 
    } 
}; 

和獨立的呼叫push_back方法:

vec.push_back(T()); // 1 
std::cout << "--- --- ---\n"; 

vec.push_back(T()); // 2 
std::cout << "--- --- ---\n"; 

現在輸出看起來更完整:

Constructor 
Move Constructor 
Destructor 
--- --- --- 
Constructor 
Move Constructor 
Copy Constructor 
Destructor 
Destructor 
--- --- --- 
Destructor 
Destructor 

第一組:

Constructor 
Move Constructor 
Destructor 

對應於第一push_back呼叫:

vec.push_back(T()); // 1 

輸出可能被容易地解密:

Constructor // Create a temporary 
Move Constructor // Move a temporary into the internal vector storage 
Destructor // Destroy a temporary 

第二組:

Constructor 
Move Constructor 
Copy Constructor 
Destructor 
Destructor 

對應於第二push_back呼叫:

vec.push_back(T()); // 2 

和一個稍微複雜一些:

Constructor // create a temporary 
Move Constructor // move it into the newly allocated vector storage 
Copy Constructor // copy previously created element into the new storage 
Destructor // destroy old storage 
Destructor // destroy temporary 

在這裏,你應該記住,矢量類內部分配的內存中,然後對其進行管理,爲所有元素提供enogh空間。因此,如果添加更多元素,則會發生新的分配,並將舊元素複製或移動到新存儲中。

在已知大小的情況下,您可以使用reserve方法,該方法僅爲特定數量的元素保留足夠的內存。它允許避免不必要的內存重新分配以及在這些重新分配期間向向量中添加新元素(至少在未超過保留大小之前)複製或移動元素。

第三組:

Destructor 
Destructor 

對應於在節目結束的矢量vec析構函數調用。

+0

這再次表明即使在測試代碼中,如果不遵守3/5/0規則,也會在屁股中咬你,以及如何通過跟隨它來避免混淆。 –

+0

很好的答案。可能很高興展示'std :: vector :: reserve'如何影響事物(與您的評論非常相關,包含向量如何「在內部分配內存然後管理它」)。 – erip

+0

@erip是的,我已經修改了答案) –

0

emplace_back對於「僅移動」類型(如std :: unique_ptr)很重要。

這是錯誤的和過於簡單化。並非所有容器都是平等創建的。爲了您的載體例如,如果我們使用reserve實現可以做到一招分配,而不是一個結構,eliding我們的複製/多餘的析構函數:

std::vector<T> v; 
v.reserve(2); 
v.emplace_back(1); 
v.emplace_back(1); 

輸出:

Constructor 
Constructor 
--- 
Destructor 
Destructor 

對於集:

std::set<T> s; 
s.emplace(1); 
s.emplace(1); 

我們得到相同的輸出。爲什麼?一套理論上應該只構造一個對象,因爲套是唯一的權利?實際上,爲了執行比較,典型的實現構造了一個臨時節點,即使它從不進入容器,也考慮了額外的構造/破壞。

+0

以及這是如何涉及_question_?如果您在評論部分回答評論,請不要回答。 –

+0

@Revolver_Ocelot對不起,因爲我在我的回答中引用了一條評論,它突然不再回答這個問題。我很高興你來這裏警察的事情。 – user8110786