簡體   English   中英

為什么emplace_back很重要?

[英]Why emplace_back does matter?

#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;
}

輸出為:

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

為什么有如此多的節制調用? 我看到:

(1)構建臨時對象temp1

(2)破壞temp1

(3)構造臨時對象temp2

(4)破壞temp2

然后,它被稱為temp1和temp 2的復制構造函數或move構造函數。因此,(5)和(6)很清楚。 但是(7)呢?

讓我們擴展一下結構:

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

在這里您應該記住, 向量類在內部分配其內存,然后對其進行管理以為所有元素提供足夠的空間。 因此,如果添加更多元素,則會發生新分配,並且會將舊元素復制或移動到新存儲中。

在已知大小的情況下,您可以使用reserve方法,該方法只是為特定數量的元素保留足夠的內存。 它允許避免在將新元素添加到向量中(至少直到不超過保留大小)的情況下,在這些重新分配期間避免不必要的內存重新分配和復制或移動元素。

第三組:

Destructor
Destructor

對應於程序結尾處的向量 vec析構函數調用。

emplace_back對於“僅移動”類型(例如std :: unique_ptr)很重要。

這是錯誤的,並且過於簡單。 並非所有容器均被平等地創建。 對於您的向量示例,如果我們使用reserve則實現可以執行移動分配而不是構造,從而消除了我們的復制/外部析構函數:

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);

我們得到相同的輸出。 為什么呢 集合在理論上應該只構造一個對象,因為集合是唯一的對嗎? 實際上,典型的實現會構造一個臨時節點以執行比較,即使它從未進入容器也要考慮額外的構造/破壞。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM