[英]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: 怎么了:
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
其初始化。
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. 这次
x
将my_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.