简体   繁体   English

向量push_back多次调用copy_constructor吗?

[英]vector push_back calling copy_constructor more than once?

I am a bit confused with the way vector push_back behaves, with the following snippet I expected the copy constructor to be invoked only twice, but the output suggest otherwise. 我对向量push_back的行为感到有些困惑,在以下代码段中,我希望复制构造函数仅被调用两次,但输出表明并非如此。 Is it a vector internal restructuring that results in this behaviour. 是导致这种行为的向量内部重组吗?

Output: 输出:

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

What happens: 怎么了:

  1. x is inserted via push_back . x是通过push_back插入的。 One copy occurs: The newly created element is initialized with the argument. 出现一个副本:用参数初始化新创建的元素。 my_int is taken over as zero because x s default constructor initialized it so. my_int被视为零,因为x的默认构造函数my_int其初始化。

  2. The second element is push_back 'd; 第二个元素是push_back ; The vector needs to reallocate the memory since the internal capacity was reached. 由于已达到内部容量 ,因此向量需要重新分配内存。
    As no move constructor is implicitly defined for Myint 1 the copy constructor is chosen; 由于没有为Myint 1隐式定义move构造函数,因此选择了复制构造函数。 The first element is copied into the newly allocated memory (its my_int is still zero... so the copy constructor shows my_int as 0 again) and then x is copied to initialize the second element (as with the first in step 1.). 第一个元素被复制到新分配的内存中(它的my_int仍然为零...因此复制构造函数再次将my_int显示为0 ),然后复制x来初始化第二个元素(与步骤1中的第一个元素一样)。 This time x has my_int set to one and that's what the output of the copy constructor tells us. 这次xmy_int设置为1,这就是复制构造函数的输出告诉我们的。

So the total amount of calls is three. 因此,通话总数为三。 This might vary from one implementation to another as the initial capacity might be different. 这可能因一个实现而异,因为初始容量可能不同。 However, two calls are be the minimum. 但是,最少要打两个电话。

You can reduce the amount of copies by, in advance, reserving more memory - ie higher the vectors capacity so the reallocation becomes unnecessary: 您可以通过预先保留更多内存来减少副本数量-即更高的向量容量,因此无需重新分配:

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

Furthermore you can elide the copies when inserting as follows: 此外,在插入时,您可以删除副本:

myints.emplace_back(0);

This "emplaces" a new element - emplace_back is a variadic template and can therefore take an arbitrary amount of arguments which it then forwards - without copies or moves - to the elements constructor. 这“放置”了一个新元素emplace_back是一个可变参数模板,因此可以接受任意数量的参数,然后将其转发给元素构造函数,而无需复制或移动。

1 Because there is a user-declared copy constructor. 1因为有一个用户声明的副本构造函数。

When the size of the vector is increased with the second push_back , the existing contents of the vector must be copied to a new buffer. 当通过第二个push_back增加向量的大小时,必须将向量的现有内容复制到新缓冲区中。 To verify, output myints.capacity() after the first push_back , it should be 1 . 要进行验证,请在第一个push_back之后输出myints.capacity() ,它应为1

This depends on how much memory was reserved to an object of type std::vector . 这取决于为std::vector类型的对象保留了多少内存。 It seems that when push_back was first executed there was allocated memory only for one element. 似乎在第一次执行push_back ,仅为一个元素分配了内存。 When the second time push_back was called the memory was reallocated to reserve memory for the second element. 当第二次调用push_back时,将重新分配内存以为第二个元素保留内存。 In this case the element that is already in the vector is copied in the new place. 在这种情况下,矢量中已经存在的元素将被复制到新位置。 And then the second element is also added. 然后添加第二个元素。

You could reserve enough memory yourself that to escape the second call of the copy constructor: 您可以自己保留足够的内存,以逃避复制构造函数的第二次调用:

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

You got it...it was the resizing. 你明白了...它正在调整大小。 But I'll just point out that if you're doing some bean counting on your constructors, you might be interested in "emplacement": 但我只是指出,如果您要依靠构造函数进行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;
}

That should give you: 那应该给你:

Inside default 
Inside default

And, due to the noexcept on the move constructor ...if you delete the reserve you'd get a move, not a copy: 而且,由于move构造函数上noexcept ...如果删除reserve您将获得移动,而不是副本:

Inside default 
Inside default 
Inside move with my_int = 0

There's no real advantage with this datatype of a move over a copy. 副本上移动的这种数据类型没有真正的优势。 But semantically it could be a big difference if your data type was more "heavy weight" and had a way of "moving" its members that was more like transferring ownership of some pointer to a large data structure. 但是从语义上讲,如果您的数据类型更“重”,并具有“移动”其成员的方式,这更像是将某个指针的所有权转移到大型数据结构,则可能会有很大的不同。

You're correct in assuming the additional invocation of the copy constructor comes from internal restructuring of the vector. 您假设复制构造函数的其他调用来自向量的内部重组是正确的。

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

Or this answer for the reason why copy construction is necessary: https://stackoverflow.com/a/11166959/3708904 或出于需要构造副本的原因而给出以下答案: https : //stackoverflow.com/a/11166959/3708904

暂无
暂无

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

相关问题 向量-push_back使用默认构造函数而不是复制构造函数 - Vector - push_back uses default constructor not copy constructor 为什么向量保持类类型会在push_back()时再调用复制构造函数一次? - Why vector hold a class type will call the copy constructor one more time when push_back()? C++ vector::push_back 使用默认复制构造函数 - C++ vector::push_back using default copy constructor 在向量上调用push_back的分段错误 - Segmentation Fault on calling push_back on a vector 向量std :: copy上的push_back - vector push_back over std::copy 是标准要求的向量重新分配和vector :: push_back使用placement new和copy构造函数吗? - is vector reallocation and vector::push_back required by the standard to use placement new and copy constructor? vector :: push_back坚持使用复制构造函数,但提供了移动构造函数 - vector::push_back insists on using copy constructor though a move constructor is provided 为什么使用std :: vector :: push_back的move变量会调用移动项的复制构造函数? - Why does using the move variant of std::vector::push_back invoke the moved item's copy constructor? 如果类型仅使用 c++11 定义复制构造函数,则使用 std::vector 时 push_back 崩溃 - push_back crashed when using std::vector if the type only defined copy constructor with c++11 当 push_back 到向量中时,我是否需要复制构造函数来修改静态成员? - Do I need a copy constructor to modify static member when push_back into a vector?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM