繁体   English   中英

向量push_back多次调用copy_constructor吗?

[英]vector push_back calling copy_constructor more than once?

我对向量push_back的行为感到有些困惑,在以下代码段中,我希望复制构造函数仅被调用两次,但输出表明并非如此。 是导致这种行为的向量内部重组吗?

输出:

 Inside default Inside copy with my_int = 0 Inside copy with my_int = 0 Inside copy with my_int = 1 
class Myint
{
private:
    int my_int;
public:
    Myint() : my_int(0) 
    {
        cout << "Inside default " << endl;
    }
    Myint(const Myint& x) : my_int(x.my_int)
    {
        cout << "Inside copy with my_int = " << x.my_int << endl;
    }

    void set(const int &x)
    {
        my_int = x;
    }
}

vector<Myint> myints;
Myint x;

myints.push_back(x);
x.set(1);
myints.push_back(x);

怎么了:

  1. x是通过push_back插入的。 出现一个副本:用参数初始化新创建的元素。 my_int被视为零,因为x的默认构造函数my_int其初始化。

  2. 第二个元素是push_back ; 由于已达到内部容量 ,因此向量需要重新分配内存。
    由于没有为Myint 1隐式定义move构造函数,因此选择了复制构造函数。 第一个元素被复制到新分配的内存中(它的my_int仍然为零...因此复制构造函数再次将my_int显示为0 ),然后复制x来初始化第二个元素(与步骤1中的第一个元素一样)。 这次xmy_int设置为1,这就是复制构造函数的输出告诉我们的。

因此,通话总数为三。 这可能因一个实现而异,因为初始容量可能不同。 但是,最少要打两个电话。

您可以通过预先保留更多内存来减少副本数量-即更高的向量容量,因此无需重新分配:

myints.reserve(2); // Now two elements can be inserted without reallocation.

此外,在插入时,您可以删除副本:

myints.emplace_back(0);

这“放置”了一个新元素emplace_back是一个可变参数模板,因此可以接受任意数量的参数,然后将其转发给元素构造函数,而无需复制或移动。

1因为有一个用户声明的副本构造函数。

当通过第二个push_back增加向量的大小时,必须将向量的现有内容复制到新缓冲区中。 要进行验证,请在第一个push_back之后输出myints.capacity() ,它应为1

这取决于为std::vector类型的对象保留了多少内存。 似乎在第一次执行push_back ,仅为一个元素分配了内存。 当第二次调用push_back时,将重新分配内存以为第二个元素保留内存。 在这种情况下,矢量中已经存在的元素将被复制到新位置。 然后添加第二个元素。

您可以自己保留足够的内存,以逃避复制构造函数的第二次调用:

vector<Myint> myints;
myints.reserve( 2 );

你明白了...它正在调整大小。 但我只是指出,如果您要依靠构造函数进行bean计数,​​您可能会对“放置”感兴趣:

#include <iostream>
#include <vector>
using namespace std;

class Myint
{
private:
    int my_int;
public:
    explicit Myint(int value = 0) : my_int(value) 
    {
        cout << "Inside default " << endl;
    }
    Myint(const Myint& x) : my_int(x.my_int)
    {
        cout << "Inside copy with my_int = " << x.my_int << endl;
    }
    Myint(const Myint&& x) noexcept : my_int(x.my_int) {
        cout << "Inside move with my_int = " << x.my_int << endl;
    } 
};

int main() {
    vector<Myint> myints;
    myints.reserve(2);

    myints.emplace_back(0);
    myints.emplace_back(1);

    // your code goes here
    return 0;
}

那应该给你:

Inside default 
Inside default

而且,由于move构造函数上noexcept ...如果删除reserve您将获得移动,而不是副本:

Inside default 
Inside default 
Inside move with my_int = 0

副本上移动的这种数据类型没有真正的优势。 但是从语义上讲,如果您的数据类型更“重”,并具有“移动”其成员的方式,这更像是将某个指针的所有权转移到大型数据结构,则可能会有很大的不同。

您假设复制构造函数的其他调用来自向量的内部重组是正确的。

请参阅此答案以获取更多详细信息: https : //stackoverflow.com/a/10368636/3708904

或出于需要构造副本的原因而给出以下答案: https : //stackoverflow.com/a/11166959/3708904

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM