Copy-and-swap
123456789101112131415161718192021222324252627282930313233343536373839404142434445 | #include <utility>
class resource {
int x = 0;
};
class foo
{
public:
foo()
: p{new resource{}}
{ }
foo(const foo& other)
: p{new resource{*(other.p)}}
{ }
foo(foo&& other)
: p{other.p}
{
other.p = nullptr;
}
foo& operator=(foo other)
{
swap(*this, other);
return *this;
}
~foo()
{
delete p;
}
friend void swap(foo& first, foo& second)
{
using std::swap;
swap(first.p, second.p);
}
private:
resource* p;
}; |
This pattern is licensed under the CC0 Public Domain Dedication.
Intent
Implement the assignment operator with strong exception safety.
Description
The copy-and-swap idiom identifies that we can implement a classes copy/move assignment operators in terms of its copy/move constructor and achieve strong exception safety.
The class foo
, on lines 7–45, has an implementation similar to the
rule of five, yet its copy and
move assignment operators have been replaced with a single
assignment operator on lines 24–29. This assignment operator takes its
argument by value, making use of the existing copy and move
constructor implementations.
To implement the assignment operator, we simply need to swap the
contents of *this
and the argument, other
. When other
goes
out of scope at the end of the function, it will destroy any
resources that were originally associated with the current object.
To achieve this, we define a swap
function for our class on
lines 36–41, which itself calls swap
on the class’s members (line 40).
We use a using-declaration on line 38 to allow swap
to be found
via argument-dependent lookup
before using std::swap
— this is not strictly necessary
in our case, because we are only swapping a pointer, but is good
practice in general. Our assignment operator then simply swaps
*this
with other
on line 26.
The copy-and-swap idiom has inherent strong exception safety
because all allocations (if any) occur when copying into the
other
argument, before any changes have been made to *this
.
It is generally, however, less optimized than a more custom
implementation of the assignment operators.
Note: We can typically avoid manual memory management and having to write the copy/move constructors, assignment operators, and destructor entirely by using the rule of zero