Ignorant
Ignorant

Reputation: 2681

How do I move dynamically allocated data from an object to another?

I have an Array class written with RAII in mind (super simplified for the purpose of this example):

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;
};

Then I have a function that takes a reference to an Array and does some operations on it. I use a temporary Array temp to perform the processing, because for several reasons I can't work directly with the reference. Once done, I would like to transfer the data from the temporary Array to the real one:

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;
}

The obvious problem is that temp goes out of scope at the end of the function: its destructor is triggered, which in turn deletes the memory. This way array would contain non-existent data.

So what is the best way to "move" the ownership of data from an object to another?

Upvotes: 3

Views: 2884

Answers (2)

einpoklum
einpoklum

Reputation: 131666

What you want is move construction or move assignment for your array class, which saves you redundant copies. Also, it will help you to use std::unique_ptr which will take care of move semantics for allocated memory for you, so that you don't need to do any memory allocation directly, yourself. You will still need to pull up your sleeves and follow the "rule of zero/three/five", which in our case is the rule of five (copy & move constructor, copy & move assignment operator, destructor).

So, try:

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);
        std::swap(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;
};

(Actually, it's a bad idea to have m_size and m_data public, since they're tied together and you don't want people messing with them. I would also template that class on the element type, i.e. T instead of int and use Array<int>)

Now you can implement your function in a very straightforward manner:

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
}

Upvotes: 8

Serge
Serge

Reputation: 12354

if you insist on your example format, then you are missing a couple of things. you need to make sure that the temp array does not destroy memory reused by the non-temp array.

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

and the function needs to do clean move

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;
}

To make it more c++ like, you can do multiple things. i.e. in the array you can create member function to move the data from other array (or a constructor, or assign operator, ...)

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

in c++11 and higher you can create a move constructor and return your value from the function:

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

there are other ways as well.

Upvotes: 2

Related Questions