【问题标题】:Why isn't move operator just destructor + move constructor为什么移动运算符不只是析构函数+移动构造函数
【发布时间】:2019-01-15 09:18:11
【问题描述】:

考虑以下sn-p:

class Foo
{
public:
    /* ... */
    Foo(Foo &&other);
    ~Foo();

    Foo &operator=(Foo &&rhs);

private:
    int *data;
};

Foo::Foo(Foo &&other)
{
    data = other.data;
    other.data = nullptr;
}

Foo::~Foo()
{
    delete data;
}

Foo &Foo::operator=(Foo &&other)
{
    if (this == &other) return *this;

    delete data;            /* SAME AS DESTRUCTOR */

    data = other.data;      /* SAME AS MOVE CONSTRUCTOR */
    other.data = nullptr;
    return *this;
}

这个 sn-p 几乎就是我最终拥有的东西。

如果可以推断出它的行为,为什么我们需要一个移动运算符?
如果此语句不正确,在这种情况下,移动运算符的行为与析构函数 + 移动构造函数的行为不同?

【问题讨论】:

  • 例如可以写成::std::swap(this->data, other.data)
  • 尽管这只是一个示例,但您应该将初始化列表与构造函数一起使用,并且您的赋值函数在所有情况下都不会返回结果。执行 this != &other 比您拥有的逻辑更可取,然后始终返回 *this。
  • Howard Hinnant 的这个答案给出了一个相关的讨论,说明为什么可能没有 single best version 的移动赋值运算符:stackoverflow.com/a/9322542/580083。另一个:stackoverflow.com/a/39864881/580083.
  • 复制交换习语基本上就是这样做的

标签: c++ move


【解决方案1】:

因为它不能被推断出来。该语言不知道拆除您的int* 所涉及的内容。也许您还有其他的家务工作要做。

事实上,如果你正在编写一个移动构造函数,你通常还有其他的事情要执行,因为如果你所做的只是deleteing 动态内存,你应该是使用智能指针,根本不需要编写自己的移动构造函数。

此外,您在代码中重复逻辑。您可以“重用析构函数”,方法是避免在移动构造函数和分配器中出现这些滑稽动作,而是简单地交换指针,然后让被移动对象的析构函数在时机成熟时做它通常会做的事情:

Foo::Foo(Foo&& other)
   : data(nullptr)
{
    *this = std::move(other);
}

Foo& Foo::operator=(Foo&& other)
{
    std::swap(this->data, other.data);
    return *this;
};

(免责声明:可能有一种更惯用的方法来做到这一点,我不记得了,但你明白了。)

现在样板文件少了很多。所以你可以看到,即使 if 语言为你推导出移动构造函数,它根本不会涉及析构函数。

【讨论】:

    【解决方案2】:

    只要对你的班级稍作改动,就可以

    class Foo
    {
    public:
        /* ... */
        // deleted copy constructor and copy assignment
        // generated destructor, move constructor and move assignment
    
    private:
        std::unique_ptr<int> data;
    };
    

    【讨论】:

      猜你喜欢
      • 2016-05-19
      • 2015-02-21
      • 2015-06-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-05-15
      • 2019-09-05
      相关资源
      最近更新 更多