如何将动态分配的数据从对象移动到另一个对象?

时间:2018-03-17 10:37:04

标签: c++ c++11 memory move-semantics temporary

我有一个用RAII编写的Array类(为了这个例子,超级简化):

struct Array
{
    Array(int size) {
        m_size = size;
        m_data = new int[m_size];
    }

    ~Array() {
        delete[] m_data;
    }

    int* m_data = nullptr;
    int  m_size = 0;
};

然后我有一个函数,它接受一个数组的引用并对它做一些操作。我使用临时数组temp来执行处理,因为有几个原因我无法直接使用引用。完成后,我想将数据从临时数据传输到真实数据:

void function(Array& array)
{
    Array temp(array.m_size * 2);

    // do heavy processing on `temp`...

    array.m_size = temp.m_size;
    array.m_data = temp.m_data;
}

明显的问题是temp超出了函数末尾的范围:它的析构函数被触发,而后者又删除了内存。这种方式array将包含不存在的数据。

那么将数据所有权从一个对象“移动”到另一个对象的最佳方法是什么?

2 个答案:

答案 0 :(得分:5)

您想要的是为阵列类移动构造或移动分配,这样可以节省多余的副本。此外,它将帮助您使用std::unique_ptr,它将为您分配内存的移动语义,因此您不需要自己直接进行任何内存分配。你仍然需要拉起袖子并按照"rule of zero/three/five",在我们的例子中是五的规则(复制和移动构造函数,复制和移动赋值运算符,析构函数)。

所以,试试:

class Array {
public:
    Array(size_t size) : m_size(size) {
        m_data = std::make_unique<int[]>(size);
    }

    Array(const Array& other) : Array(other.size) {
        std::copy_n(other.m_data.get(), other.m_size, m_data.get());
    }

    Array(Array&& other) : m_data(nullptr) {
        *this = other;
    } 

    Array& operator=(Array&& other) {
        std::swap(m_data, other.m_data);
        m_size = other.m_size;
        return *this;
    }

    Array& operator=(const Array& other) {
        m_data = std::make_unique<int[]>(other.m_size);
        std::copy_n(other.m_data.get(), other.m_size, m_data.get());
        return *this;
    }

    ~Array() = default;

    std::unique_ptr<int[]>  m_data;
    size_t                  m_size;
};

(实际上,让m_sizem_data公开是一个坏主意,因为他们被捆绑在一起并且你不希望别人搞砸他们。我也会在元素类型上模拟该类,即T而不是int并使用Array<int>

现在您可以非常直接的方式实现您的功能:

void function(Array& array)
{
    Array temp { array }; // this uses the copy constructor

    // do heavy processing on `temp`...

    std::swap(array, temp); // a regular assignment would copy
}

答案 1 :(得分:2)

如果你坚持你的示例格式,那么你会遗漏几件事。你需要确保临时数组不会破坏非临时数组重用的内存。

 ~Array() {
    if (md_data != nullptr) 
         delete [] m_data;
 }

并且该功能需要做清洁移动

void function(Array& array)
{
    Array temp(array.m_size * 2);

    // do heavy processing on `temp`...

    array.m_size = temp.m_size;
    array.m_data = temp.m_data;
    temp.m_data = nullptr;
    temp.m_size = 0;
}

为了使它更像c ++,你可以做多件事。即在数组中,您可以创建成员函数以从其他数组(或构造函数,或赋值运算符,...)移动数据。

  void Array::move(Array &temp) {
      m_size = temp.m_size;
      temp.m_size = 0;
      m_data = temp.m_data;
      temp.m_data = 0;
  }

在c ++ 11及更高版本中,您可以创建一个移动构造函数并从函数中返回值:

 Array(Array &&temp) {
      do your move here
 }
 Array function() {
      Array temp(...);
      return temp;
 }

还有其他方法。