将临时对象移动到向量中
#include <iostream>
#include <utility>
#include <vector>
int i = 0;
struct A
{
A() : j( ++i )
{
std::cout<<"constructor "<<j<<std::endl;
}
A( const A & c) : j(c.j)
{
std::cout<<"copy "<<j<<std::endl;
}
A( const A && c) : j(c.j)
{
std::cout<<"move "<<j<<std::endl;
}
~A()
{
std::cout<<"destructor "<<j<<std::endl;
}
int j;
};
typedef std::vector< A > vec;
void foo( vec & v )
{
v.push_back( std::move( A() ) );
}
int main()
{
vec v;
foo( v );
foo( v );
}
上面的示例产生下一个输出:
constructor 1
move 1
destructor 1
constructor 2
move 2
move 1
destructor 1
destructor 2
destructor 1
destructor 2
问题:
- 为什么执行第一个析构函数(但不为第二个对象执行它)?
- 为什么第二个对象的移动在第一个对象的移动之前执行?
- 为什么最后每个对象执行两个析构函数?
PS我刚刚检查过,对象确实按预期放置(第一个位于向量中的位置 0,第二个位于向量中的位置 1)
PPS 如果重要的话,我正在使用 gcc 4.3,并且我像这样编译程序:
g++ n1.cpp -Wall -Wextra -pedantic -ansi -std=c++0x -O3
#include <iostream>
#include <utility>
#include <vector>
int i = 0;
struct A
{
A() : j( ++i )
{
std::cout<<"constructor "<<j<<std::endl;
}
A( const A & c) : j(c.j)
{
std::cout<<"copy "<<j<<std::endl;
}
A( const A && c) : j(c.j)
{
std::cout<<"move "<<j<<std::endl;
}
~A()
{
std::cout<<"destructor "<<j<<std::endl;
}
int j;
};
typedef std::vector< A > vec;
void foo( vec & v )
{
v.push_back( std::move( A() ) );
}
int main()
{
vec v;
foo( v );
foo( v );
}
The example above produces next output :
constructor 1
move 1
destructor 1
constructor 2
move 2
move 1
destructor 1
destructor 2
destructor 1
destructor 2
Questions :
- Why is the 1st destructor executed (but it isn't executed for the 2nd object)?
- Why is move of the 2nd object, executed before the move of the 1st object?
- Why are at the end two destructors for each object executed?
PS I just checked, and the objects are indeed placed as expected (the 1st goes to the position 0 in the vector, and the 2nd goes to the position 1 in the vector)
PPS If it matters, I am using gcc 4.3, and I compile the program like this :
g++ n1.cpp -Wall -Wextra -pedantic -ansi -std=c++0x -O3
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
我稍微重新编码了您的示例:
push_back
中删除了std::move
(它是多余的)。foo
的调用之间插入了标记。对我来说,这打印出来的结果与您的代码类似:
第二个析构函数在标记为
// 1
的行处对第二个对象执行。这是在第二次调用push_back
结束时销毁临时A()
。注意:对我来说,第一个对象是复制的,而不是移动的,更多信息如下。
答:异常安全。
解释:在此
push_back
期间,向量发现它有一个已满的缓冲区(一个),并且需要创建一个可容纳两个的新缓冲区。它创建新的缓冲区。然后将第二个对象移动到该缓冲区中(在其末尾)。如果该构造引发异常,则原始缓冲区仍然完好无损,并且向量保持不变。否则,元素将从第一个缓冲区移动或复制到第二个缓冲区(从而第二个移动/复制第一个元素)。如果
A
有一个noexcept
移动构造函数,则将使用move
将其从旧缓冲区移动到新缓冲区。但是,如果移动构造函数不是noexcept
,则将使用copy
。这又是为了异常安全。如果从旧缓冲区到新缓冲区的移动可能失败,则必须保持旧缓冲区完好无损,以便向量
可以恢复到其原始状态。如果我将
noexcept
添加到您的移动构造函数中:那么我的输出是:
注意标记为
// 2
的行是旧缓冲区中第一个元素的销毁,在它具有已移动构建到新缓冲区中。这标志着
vector
的破坏,从而标记vector
的每个元素的破坏。I've slightly recoded your example:
const
from the move constructor.std::move
from thepush_back
(it is superfluous).foo
.For me this prints out similar to your code:
The 2nd destructor is executed for the 2nd object at the line marked
// 1
. This is the destruction of the temporaryA()
at the end of the second call topush_back
.Note: For me the 1st object is copied, not moved, more about that below.
Answer: Exception safety.
Explanation: During this
push_back
the vector discovers it has a full buffer (of one) and needs to create a new buffer with room for two. It creates the new buffer. Then moves the second object into that buffer (at the end of it). If that construction throws an exception, the original buffer is still intact and thevector
remains unchanged. Otherwise the elements are moved or copied from the first buffer to the second buffer (thus move/copying the first element second).If
A
has anoexcept
move constructor amove
will be used to move it from the old buffer to the new. However if the move constructor is notnoexcept
then acopy
will be used. This is again for exception safety. If the movement from the old buffer to the new can fail, then the old buffer must be left intact so that thevector
can be restored to its original state.If I add
noexcept
to your move constructor:Then my output is:
Note the line marked
// 2
is the destruction of the first element from the old buffer, after it has been move constructed into the new buffer.This is marking the destruction of the
vector
and thus the destruction of each of thevector
's elements.明智地使用
reserve
可以解决一半的问题:http://ideone.com/5Lya6< /a> 通过减少意外移动的数量(您没有明确请求)。另外,不要忘记,temp 的析构函数在移动到向量后仍然会触发。这就是为什么即使在移动分配/构造之后,您也必须确保温度保持在正常、可破坏的状态。
Judicious use of
reserve
solves half your problem: http://ideone.com/5Lya6 by reducing the number of unexpected moves (that you don't explicitely request)Also don't forget that the temp's destructor will still fire after having being moved into the vector. This is why you have to make sure that the temp remains in sane, destructible state even after the move assignment/construction.
向量在调用
push_back
期间增加其容量并移动内部元素。The vector is increasing its capacity and moving the internal elements during the call to
push_back
.移动构造函数不会“销毁”移动的对象。
输出给出:
A move constructor does not "destroy" the moved object.
The output gives: