c++ - 编写 move 复制和 move 赋值构造函数的有效方法

标签 c++ constructor c++11 variable-assignment move

下面的赋值和复制 move 构造函数是最高效的吗? 如果有人有其他方式请告诉我? 我的意思是 std::swap 怎么样?并且在下面的代码中通过复制构造函数调用赋值是安全的吗?

#include <iostream>
#include <functional>
#include <algorithm>
#include <utility>

using std::cout;
using std::cin;
using std::endl;
using std::bind;


class Widget
{

public:

    Widget(int length)
        :length_(length),
        data_(new int[length])
    {
        cout<<__FUNCTION__<<"("<<length<<")"<<endl;
    }

    ~Widget()
    {
        cout<<endl<<__FUNCTION__<<"()"<<endl;
        if (data_)
        {
            cout<<"deleting source"<<endl;
        } 
        else
        {
            cout<<"deleting Moved object"<<endl;
        }

        cout<<endl<<endl;
    }

    Widget(const Widget& other)
        :length_(other.length_),
        data_(new int[length_])
    {
        cout<<__FUNCTION__<<"(const Widget& other)"<<endl;
        std::copy(other.data_,other.data_ + length_,data_);
    }

    Widget(Widget&& other)
/*
        :length_(other.length_),
        data_(new int[length_])*/
    {
        cout<<__FUNCTION__<<"(Widget&& other)"<<endl;
        length_ = 0;
        data_ = nullptr;
        std::swap(length_,other.length_);
        std::swap(data_,other.data_);
    }

    Widget& operator = (Widget&& other)
    {
        cout<<__FUNCTION__<<"(Widget&& other)"<<endl;

        std::swap(length_,other.length_);
        std::swap(data_,other.data_);

        return *this;
    }

    Widget& operator = (const Widget& other)
    {
        cout<<__FUNCTION__<<"(const Widget& other)"<<endl;
        Widget tem(other);
        std::swap(length_,tem.length_);
        std::swap(data_,tem.data_);

        return *this;
    }
    int length()
    {
        return length_;
    }

private:

    int length_;
    int* data_;
};



int main()
{
    {
        Widget w1(1);
        Widget w2(std::move(Widget(2)));

        w1 = std::move(w2);
    }


    cout<<"ENTER"<<endl;
    cin.get();
    return 0;
}

最佳答案

从效率 POV 来看看起来不错,但包含大量重复代码。我愿意

  • 为您的类实现一个 swap() 运算符。
  • 在声明的地方初始化 length_data_
  • 尽可能根据其他操作来实现操作。

可能想要使用std::memcpy 而不是std::copy 因为无论如何您正在处理原始数组。一些编译器会为你做这些,但可能不是全部......

这是您的代码的去重版本。注意只有 一个 地方需要知道 Widget 的两个实例是如何交换的。并且只有一个地方知道如何分配给定大小的 Widget。

编辑:您通常还希望使用依赖于参数的查找来定位交换,以防万一您有非原始成员。

编辑:整合了@Philipp 的建议,即让赋值运算符按值获取参数。这样,它既可以作为 move 赋值运算符,也可以作为复制赋值运算符。在 move 的情况下,不是说如果你传递一个临时的,它不会被复制,因为 move 构造函数,不是复制构造函数将被用来传递参数.

编辑:C++11 允许在右值上调用非成本成员,以与以前版本的标准兼容。这允许像 Widget(...) = someWidget 这样奇怪的代码可以编译。通过在声明之后放置 & 来使 operator= 需要 this 的左值可以防止这种情况发生。请注意,即使没有该限制,代码也是正确的,但它似乎是个好主意,所以我添加了它。

编辑:正如 Guillaume Papin 指出的那样,析构函数应该使用 delete[] 而不是普通的 delete。 C++ 标准要求通过 delete [] 删除通过 new [] 分配的内存,即它允许 new' 和 new []`使用不同的堆。

class Widget
{
public:
    Widget(int length)
        :length_(length)
        ,data_(new int[length])
    {}

    ~Widget()
    {
        delete[] data_;
    }

    Widget(const Widget& other)
        :Widget(other.length_)
    {
        std::copy(other.data_, other.data_ + length_, data_);
    }

    Widget(Widget&& other)
    {
        swap(*this, other);
    }

    Widget& operator= (Widget other) &
    {
        swap(*this, other);
        return *this;
    }

    int length() const
    {
        return length_;
    }

private:
    friend void swap(Widget& a, Widget& b);

    int length_ = 0;
    int* data_ = nullptr;
};

void swap(Widget& a, Widget& b) {
    using std::swap;
    swap(a.length_, b.length_);
    swap(a.data_, b.data_);
}

关于c++ - 编写 move 复制和 move 赋值构造函数的有效方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12651063/

相关文章:

c++ - 在 C++ 中 x 是整数时 "if (x) { … }"的含义是什么?

c++ - 如何创建返回与函数具有相同签名的仿函数的函数?

c++ - 通过显式构造函数初始化数组

c++ - 如何在C++中基于标签运行特定功能?

c++ - CMake:将静态库从子目录链接到另一个子目录中的可执行文件

c++ - 无法从命令行正确获取数据

c++ - 以下内容是否确实违反了 ODR?

c# - 为什么 SecondChild 类的覆盖方法没有被调用两次?

C++ "No matching constructor for initialization of"编译器错误

java - 类中的实例变量或在 Java 的构造函数中将它们用作参数有什么区别?