为什么emplace_back很重要?

时间:2017-06-04 14:24:49

标签: c++ c++11

#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)destruct temp1

(3)构造临时对象temp2

(4)破坏temp2

然后它被称为临时构造函数或移动构造函数的temp1和temp 2.因此,(5)和(6)是明确的。但是(7)呢?

2 个答案:

答案 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

在这里你应该记住 vector 类在内部分配它的内存然后管理它以为所有元素提供enogh空间。因此,如果添加更多元素,则会发生新分配,并将旧元素复制或移动到新存储中。

如果已知大小,您可以使用reserve方法,该方法只为特定数量的元素保留足够的内存。它允许在重新分配期间避免不必要的内存重新分配以及复制或移动元素,以便在向量中添加新元素(至少在您不超过保留大小之前)。

第三组:

Destructor
Destructor

对应于程序末尾的 vector vec析构函数调用。

答案 1 :(得分:0)

  

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

我们得到相同的输出。为什么呢?理论上,一个集合应该只构造一个对象,因为集合是唯一的吗?实际上,典型的实现构造一个临时节点以便执行比较,考虑额外的构造/破坏,即使它从未进入容器。