2017-06-04 85 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

相关问题