3 minutes read 10th December, 2014
programming

The copy and swap idiom is a design pattern for the C++ copy assignment operators. When implementing a class that manages a resource, there are three functions that are implicitly created for you if you do not define them: the copy constructor, the destructor and the copy assignment operator.

Assuming you already have a class that manages a resource with constructor, copy constructor and destructor the copy and swap idiom gives you an elegant implementation of the copy assignment operator that doesn’t duplicate code and has a strong exception guarantee.

Given a simple class such as the one below which manages an array of memory, how do we implement the copy assignment operator?

class MyArray {
    int *mem;
    size_t len;

public:
    MyArray();                      // Default constructor
    MyArray(const MyArray &that);   // Copy constructor
    ~MyArray();                     // Destructor
};

We need these three functions defined. Also the copy constructor needs to statisfy the basic exception guaratee, that is in the case of an exception it must not leak memory[2]. Assuming we have that let’s get on with copy and swap!

The swap function

First we need a swap function which is half of the “copy and swap”. The swap function is a non-throwing function that swaps two of our objects. We cannot just use std::swap directly on our class as it would attempt to use the assignment operator to swap the objects! Below is a swap friend function for our MyArray class:

friend void swap(MyArray &a, MyArray &b) {
    std::swap(a.mem, b.mem);
    std::swap(a.len, b.len);
}

It doesn’t need to be anything fancy, std::swap is fine for swapping primitive types such as pointers and integers. Remember though, don’t let swap throw exceptions. Luckily this is easy as most swap functions can be made just by calling std::swap on each pair of members.

The copy assignment operator

The assignment operator is a function that takes another object and assigns it to the current object. When you write something like MyArray points = MyArray(); you are first creating an array object points and then assigning the new object MyArray() to it.

MyArray& operator=(MyArray that) {
    swap(*this, that);
    return *this;
}

Let’s break down what’s happening here. First we create a copy of that array by passing it to the function by value. Doing so causes the compiler to implicitly call our copy constructor for us. As the assignment operator assigns the passed object to this object we next swap the members of that array with this array. When we return that array is destroyed automatically as it goes out of scope.

“But what about that? You’ve overwritten it with the old this members!” We’ve overwritten the copied that array, the original still exists.

“What about freeing memory on this before swapping?” The old members of this are first transfered to the that object, and are then freed when that goes out of scope at the end of the assignment operator function.

“And this is strongly exception safe?” Yup, the only place we can throw exceptions is in copying that, at which point this has not been modified.

Putting it all together

Our final MyArray class would look like this:

class MyArray {
    int *mem;
    size_t len;

public:
    MyArray();                      // Default constructor
    MyArray(const MyArray &that);   // Copy constructor
    ~MyArray();                     // Destructor

    // Swap function
    friend void swap(MyArray &a, MyArray &b) {
        std::swap(a.mem, b.mem);
        std::swap(a.len, b.len);
    }

    // Copy assignment operator using copy and swap.
    MyArray& operator=(MyArray that) {
        swap(*this, that);
        return *this;
    }
};

It’s simple, it’s elegant and if something fails in copying the object, nothing is changed (the strong exception guarantee).

C++11?

The copy and swap idiom remains the same in C++11 except we have a new important function to define for our class, the move constructor. The move constructor is a constructor that takes the resources from another class instance, but leaves that class in an assignable and destructable state. The move constructor is very easy given we have already implemented a swap function:

MyArray(MyArray&& that): MyArray() {
    swap(*this, that);
}

So how does this work? First we initialise this object with the default constructor using constructor delegation (some compilers don’t allow this even though it’s a C++11 feature, in which case you have to construct this manually). Then we swap the two objects to move the members to this. that object is left with the default constructed members, leaving it in a state that can be both assigned to and destructed whilst reusing the swap function again.