std::emplace_back 调用向量中其他对象的构造函数?

Posted

技术标签:

【中文标题】std::emplace_back 调用向量中其他对象的构造函数?【英文标题】:std::emplace_back invoking constructor of other objects in vector? 【发布时间】:2020-03-07 16:35:16 【问题描述】:

我真的对std::vector::emplace_back 感到困惑。我运行以下代码:

struct Thing 
    explicit Thing(std::string name) : name_std::move(name) 
        std::cout << "Constructed a thing called " << name_ << std::endl;
    

    ~Thing() 
        std::cout << "Deconstructed a thing called " << name_ << std::endl;
    ;
    std::string name_;
;


int main() 
    std::vector<Thing> thingsThing("A");
    std::cout << "Size: " << things.size() << std::endl;
    things.emplace_back("B");
    std::cout << "Size: " << things.size() << std::endl;

并得到这个输出:

Constructed a thing called A
Deconstructed a thing called A
Size: 1
Constructed a thing called B
Deconstructed a thing called A
Size: 2
Deconstructed a thing called A
Deconstructed a thing called B

到底为什么things.emplace_back("B") 会调用 A 的解构器?

【问题讨论】:

添加第二个元素是调用向量的调整大小,将旧元素复制到新向量,以及销毁旧元素。您的对象有一个默认的复制构造函数。创建一个具有类似 cout 的显式复制构造函数(和一个移动构造函数),您将看到正在复制或移动构造的新 A 对象。 请注意,C++ 术语是析构函数,而不是解构函数。 【参考方案1】:

std::vector 将对象连续存储在分配的内存块中。当该块的大小变得太小而无法向其中添加新元素时,它必须分配一个新的更大的内存块并将向量中当前存在的元素复制/移动到该新分配中。之后,之前分配中的对象被销毁,涉及到析构函数调用。

您没有看到正在制作的副本,因为您没有定义自定义复制构造函数。如果你这样做了,你会看到A 的副本是在原始A 被破坏之前构造的副本。如果定义noexcept 移动构造函数,您将看到A 的副本在原始A 被破坏之前被移动构造。

【讨论】:

世界再次变得有意义。谢谢!【参考方案2】:

发生的情况是,当您将 B 添加到向量时,该向量可能只有 1 个元素的容量,因此必须将其现有元素移动到另一个向量中(破坏原始存储中的那个)

让我们添加更多日志来看看会发生什么:

#include <iostream>
#include <string>
#include <vector>

struct Thing 
    explicit Thing(std::string name) : name_std::move(name) 
        std::cout << "Constructed a thing called " << name_ << " at " << this << std::endl;
    
    Thing(Thing&& t) noexcept : name_std::move(t.name_) 
        std::cout << "Moved a thing called " << name_ << " from " << &t << " to " << this << std::endl;
    
    Thing(const Thing& t) : name_t.name_ 
        std::cout << "Copied a thing called " << name_ << " from " << &t << " to " << this << std::endl;
    

    ~Thing() 
        std::cout << "Deconstructed a thing called " << name_ << " at " << this << std::endl;
    ;
    std::string name_;
;


int main() 
    std::vector<Thing> thingsThing("A");
    std::cout << "Size: " << things.size() << " Capacity: " << things.capacity() << std::endl;
    things.emplace_back("B");
    std::cout << "Size: " << things.size() << " Capacity: " << things.capacity() << std::endl;

示例输出:

Constructed a thing called A at 0x1111
Copied a thing called A from 0x1111 to 0x2222
Deconstructed a thing called A at 0x1111
Size: 1 Capacity: 1
Constructed a thing called B at 0x3333
Moved a thing called A from 0x2222 to 0x4444
Deconstructed a thing called A at 0x2222
Size: 2 Capacity: 2
Deconstructed a thing called A at 0x4444
Deconstructed a thing called B at 0x3333

【讨论】:

以上是关于std::emplace_back 调用向量中其他对象的构造函数?的主要内容,如果未能解决你的问题,请参考以下文章

C++ 向量到 Python 3.3

为啥最后一个推回对象的字段到向量中会转移到向量的其他对象? [关闭]

通过从向量的其他元素中减去向量的每个元素来制作矩阵

Armadillo C++:根据其他两个向量对向量进行排序

函数中的c ++大向量多次调用

如何通过引用返回一个向量,隐含地期望一个空向量作为输入?