【问题标题】:Support for move semantic in vector [duplicate]支持向量中的移动语义[重复]
【发布时间】:2019-08-04 19:26:39
【问题描述】:

首先,我英语不好,所以我使用翻译。

这段代码是支持vector中移动语义的代码。

创建单个向量并将 Test 对象插入 push_back() 方法的代码。

这段代码的结果是,每次因为push_back()方法添加一个Test对象,都应该调用move构造函数,对吧?

但是编译器只有在插入新对象时才调用移动构造函数,而旧对象调用复制构造函数。

为什么会这样?我错过了什么吗?

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

class Test
{
public:
    Test(int data1, int data2) :data1(data1), data2(data2) 
    {
        cout << "Normal Constructor" << endl; 
    }
    Test(const Test& src)
    {
        cout << "Copy Constructor" << endl;
        this->data1 = src.data1;
        this->data2 = src.data2;
    }
    Test(Test&& rhs)
    {
        cout << "Move Constructor" << endl;
        this->data1 = rhs.data1;
        this->data2 = rhs.data2;
        rhs.data1 = 0;
        rhs.data2 = 0;
    }
    Test& operator=(const Test& src)
    {
        if (this == &src)
            return *this;
        cout << "Copy assignment operator" << endl;
        this->data1 = src.data1;
        this->data2 = src.data2;
        return *this;
    }
    Test& operator=(Test&& rhs) 
    {
        if (this == &rhs)
            return *this;
        cout << "Move assignment operator" << endl;
        this->data1 = rhs.data1;
        this->data2 = rhs.data2;
        rhs.data1 = 0;
        rhs.data2 = 0;
        return *this;
    }
    private:
        int data1, data2;
    };

int main()
{
    vector<Test> vec;
    for (int i = 0; i < 5; i++)
    {
        cout << "Iteration  " << i << endl;
        vec.push_back(Test(100, 100));
        cout << endl;
    }
}

【问题讨论】:

  • 同意重复,但tkausl的回答更好

标签: c++


【解决方案1】:

std::vector 选择复制构造函数而不是移动构造函数的原因是您的移动构造函数不是nothrow。为什么std::vector 关心?

好吧,std::vector 有一些异常安全保证。例如,参见std::vector::push_backs 异常保证:

如果抛出异常(可能是由于 Allocator::allocate() 或元素复制/移动构造函数/赋值),则此函数无效(强异常保证)。

这意味着当抛出异常时以某种方式,它不会炸毁整个容器,在这种情况下,strong 异常保证甚至可以保证您错误插入对向量完全没有影响

拥有非noexcept 移动构造函数是个问题,因为如果移动会引发重新分配,std::vector 可能已经移动了一半,因此无法返回到有效状态它的项目超过但不是另一半,并且试图将它们移回旧的内存位置也可能会抛出。复制没有这个问题,因为旧项目保持不变,所以如果一个副本会抛出,std::vector 只需要破坏它迄今为止复制的所有(新)项目并放弃。作为集合的用户,您可以保证错误的插入或重新分配会使所有其他项目保持原样。这就是为什么它只会在保证移动永远不会抛出的情况下才使用移动构造函数,这是noexcept 所承诺的。

【讨论】:

  • 感谢您的回复。这很有帮助。再问你一个问题,我知道 std::vector 没有使用没有 noexcept 的移动构造函数。顺便说一句,为什么编译器使用移动构造函数来添加新元素?移动构造函数没有声明为 noexcept。
  • 它可以移动新元素,因为此时它是否有效并不重要。重新分配已经发生并成功,因此移动构造的异常根本不会触及其他元素。重新分配的问题是确保移动所有元素或不移动元素,而不仅仅是部分元素。单个元素没有这个问题。
猜你喜欢
  • 2021-07-24
  • 2020-12-07
  • 1970-01-01
  • 2023-03-29
  • 1970-01-01
  • 2013-01-14
  • 2015-10-03
  • 2016-11-02
  • 1970-01-01
相关资源
最近更新 更多