gpt4 book ai didi

c++ - 将临时对象移动到 vector 中

转载 作者:可可西里 更新时间:2023-11-01 17:09:16 27 4
gpt4 key购买 nike

#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

问题:

  1. 为什么执行了第一个析构函数(但没有为第二个对象执行)?
  2. 为什么第二个对象的移动在第一个对象的移动之前执行?
  3. 为什么最后每个执行的对象都有两个析构函数?

PS 我刚刚检查了一下,确实按预期放置了对象(第一个到 vector 中的位置 0,第二个到 vector 中的位置 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( 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( A() );
}

int main()
{
vec v;
std::cout << "A\n";
foo( v );
std::cout << "B\n";
foo( v );
std::cout << "C\n";
}
  1. 我已经从移动构造函数中删除了 const
  2. 我已经从 push_back 中删除了 std::move(这是多余的)。
  3. 我在对 foo 的调用之间插入了标记。

对我来说,这与您的代码类似:

A
constructor 1
move 1
destructor 1
B
constructor 2
move 2
copy 1
destructor 1
destructor 2 // 1
C
destructor 2
destructor 1
  1. Why is the 1st destructor executed (but it isn't executed for the 2nd object)?

第二个析构函数在标记为 //1 的行为第二个对象执行。这是在第二次调用 push_back 结束时销毁临时 A()

  1. Why is move of the 2nd object, executed before the move of the 1st object?

注意:对我来说,第一个对象是复制的,而不是移动的,下面会详细介绍。

答案:异常安全。

解释:在这个push_back 过程中,vector 发现它有一个完整的缓冲区(一个)并且需要创建一个新的缓冲区来容纳两个缓冲区。它创建新的缓冲区。然后将第二个对象移动到该缓冲区(在它的末尾)。如果该构造抛出异常,原始缓冲区仍然完好无损,vector 保持不变。否则,元素将从第一个缓冲区移动或复制到第二个缓冲区(因此第二个移动/复制第一个元素)。

如果 A 有一个 noexcept 移动构造函数,一个 move 将用于将它从旧缓冲区移动到新缓冲区。但是,如果移动构造函数不是 noexcept,则将使用 copy。这又是为了异常安全。如果从旧缓冲区到新缓冲区的移动失败,则必须保持旧缓冲区完好无损,以便 vector 可以恢复到其原始状态。

如果我将 noexcept 添加到您的移动构造函数中:

A( A && c) noexcept : j(c.j)
{
std::cout<<"move "<<j<<std::endl;
}

那么我的输出是:

A
constructor 1
move 1
destructor 1
B
constructor 2
move 2
move 1
destructor 1 // 2
destructor 2
C
destructor 2
destructor 1

请注意标记为 //2 的行是旧缓冲区中第一个元素的破坏,在它被移动构造到新缓冲区中之后。

  1. Why are at the end two destructors for each object executed?

这标志着 vector 的破坏,从而破坏了 vector 的每个元素。

关于c++ - 将临时对象移动到 vector 中,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7671697/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com