Search code examples
c++c++11memorymove-semanticstemporary

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?


Solution

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