使用移动构造函数与传递指针有什么优势?

What's the advantage of using move constructor vs passing a pointer?

本文关键字:什么 指针 移动 构造函数      更新时间:2023-10-16
Foo(Foo&& other) {
this->bar = other.bar;
other.bar = nullptr;
}
Foo(Foo* other) {
this->bar = other->bar;
other->bar = nullptr;
}

以上两个似乎只是在做同样的事情。那么为什么建议使用move构造函数呢?它提供了什么优势?

使用正确的移动构造函数的原因有很多:

  1. 公约。考虑以下代码:

    Foo foo;
    Foo bar(std::move(foo));
    

    如果您看到该代码,就非常清楚您要做什么:您正在将foo移动到bar。相比之下:

    Foo foo;
    Foo bar(&foo);
    

    这是什么意思?它是否存储了指向另一个Foo的指针?Foo是某种链表节点类型吗?您必须在文档中查找Foo的基于指针的构造函数。

  2. 无法获取指向prvalues的指针。Foo bar(Foo{})将(在C++17之前)创建一个临时的prvalue,然后从它移动到bar中(该移动可以在C++17之前被消除)。但你不能这样做:Foo bar(&Foo{})

  3. 你可以从有意义的地方自动移动。C++17的广义省略使许多自动移动消失了,但仍有一些保留:

    Foo function()
    {
    Foo foo;
    //Do stuff
    return foo;
    }
    

    这将从foo移动,而不必调用std::move。原因是foo是一个即将被破坏的局部变量。以这种方式返回它意味着从它移动到返回值是完全合理的。

    您的方法需要显式地编写return &foo。这使得auto返回类型推导变得相当困难。这样的函数将推导出一个Foo*,这意味着您刚刚返回了一个悬空指针。而如果你做return foo;,它会推导出Foo的prvalue,并自动移动(可能会被忽略)。

  4. 无需进行nullptr检查。如果你取一个Foo*,可能有人会传递一个空指针。而有了引用,就不太可能了(而且他们已经调用了UB)。

如果我们想用现有的语言结构来支持移动,我们只需要使用非const引用来表示"移动",而不是指针。通过给它自己的语法,让它清楚地表明我们什么时候在移动,什么时候不在移动。

相关文章: