2138

What is this idiom and when should it be used? Which problems does it solve? Does the idiom change when C++11 is used?

Although it's been mentioned in many places, we didn't have any singular "what is it" question and answer, so here it is. Here is a partial list of places where it was previously mentioned:

Community
  • 1
  • 1
GManNickG
  • 459,504
  • 50
  • 465
  • 534
  • 7
    http://gotw.ca/gotw/059.htm from Herb Sutter – DumbCoder Jul 19 '10 at 08:51
  • 2
    Awesome, I linked this question from my [answer to move semantics](http://stackoverflow.com/questions/3106110/can-someone-please-explain-move-semantics-to-me/3109981#3109981). – fredoverflow Jul 19 '10 at 09:02
  • 4
    Good idea to have a full-fledge explanation for this idiom, it's so common that everyone should know about it. – Matthieu M. Jul 19 '10 at 11:52
  • 18
    Warning: The copy/swap idiom is used far more frequently than it is useful. It is often harmful to performance when a strong exception safety guarantee is not needed from copy assignment. And when strong exception safety is needed for copy assignment, it is easily provided by a short generic function, in addition to a much faster copy assignment operator. See http://www.slideshare.net/ripplelabs/howard-hinnant-accu2014 slides 43 - 53. Summary: copy/swap is a useful tool in the toolbox. But it has been over-marketed and subsequently has often been abused. – Howard Hinnant Mar 16 '16 at 03:08
  • 2
    @HowardHinnant: Yeah, +1 to that. I wrote this at a time where nearly every C++ question was "help my class crashes when a copy it" and this was my response. It's appropriate when you just want working copy-/move-semantics or whatever so you can move on to other things, but it's not really optimal. Feel free to put a disclaimer at the top of my answer if you think that'll help. – GManNickG Mar 16 '16 at 03:57
  • Thanks @GManNickG. There is now a video presentation of these slides available here: http://www.youtube.com/watch?v=vLinb2fgkHk&t=35m30s – Howard Hinnant Nov 23 '16 at 22:27

5 Answers5

2331

Overview

Why do we need the copy-and-swap idiom?

Any class that manages a resource (a wrapper, like a smart pointer) needs to implement The Big Three. While the goals and implementation of the copy-constructor and destructor are straightforward, the copy-assignment operator is arguably the most nuanced and difficult. How should it be done? What pitfalls need to be avoided?

The copy-and-swap idiom is the solution, and elegantly assists the assignment operator in achieving two things: avoiding code duplication, and providing a strong exception guarantee.

How does it work?

Conceptually, it works by using the copy-constructor's functionality to create a local copy of the data, then takes the copied data with a swap function, swapping the old data with the new data. The temporary copy then destructs, taking the old data with it. We are left with a copy of the new data.

In order to use the copy-and-swap idiom, we need three things: a working copy-constructor, a working destructor (both are the basis of any wrapper, so should be complete anyway), and a swap function.

A swap function is a non-throwing function that swaps two objects of a class, member for member. We might be tempted to use std::swap instead of providing our own, but this would be impossible; std::swap uses the copy-constructor and copy-assignment operator within its implementation, and we'd ultimately be trying to define the assignment operator in terms of itself!

(Not only that, but unqualified calls to swap will use our custom swap operator, skipping over the unnecessary construction and destruction of our class that std::swap would entail.)


An in-depth explanation

The goal

Let's consider a concrete case. We want to manage, in an otherwise useless class, a dynamic array. We start with a working constructor, copy-constructor, and destructor:

#include <algorithm> // std::copy
#include <cstddef> // std::size_t

class dumb_array
{
public:
    // (default) constructor
    dumb_array(std::size_t size = 0)
        : mSize(size),
          mArray(mSize ? new int[mSize]() : nullptr)
    {
    }

    // copy-constructor
    dumb_array(const dumb_array& other)
        : mSize(other.mSize),
          mArray(mSize ? new int[mSize] : nullptr)
    {
        // note that this is non-throwing, because of the data
        // types being used; more attention to detail with regards
        // to exceptions must be given in a more general case, however
        std::copy(other.mArray, other.mArray + mSize, mArray);
    }

    // destructor
    ~dumb_array()
    {
        delete [] mArray;
    }

private:
    std::size_t mSize;
    int* mArray;
};

This class almost manages the array successfully, but it needs operator= to work correctly.

A failed solution

Here's how a naive implementation might look:

// the hard part
dumb_array& operator=(const dumb_array& other)
{
    if (this != &other) // (1)
    {
        // get rid of the old data...
        delete [] mArray; // (2)
        mArray = nullptr; // (2) *(see footnote for rationale)

        // ...and put in the new
        mSize = other.mSize; // (3)
        mArray = mSize ? new int[mSize] : nullptr; // (3)
        std::copy(other.mArray, other.mArray + mSize, mArray); // (3)
    }

    return *this;
}

And we say we're finished; this now manages an array, without leaks. However, it suffers from three problems, marked sequentially in the code as (n).

  1. The first is the self-assignment test.
    This check serves two purposes: it's an easy way to prevent us from running needless code on self-assignment, and it protects us from subtle bugs (such as deleting the array only to try and copy it). But in all other cases it merely serves to slow the program down, and act as noise in the code; self-assignment rarely occurs, so most of the time this check is a waste.
    It would be better if the operator could work properly without it.

  2. The second is that it only provides a basic exception guarantee. If new int[mSize] fails, *this will have been modified. (Namely, the size is wrong and the data is gone!)
    For a strong exception guarantee, it would need to be something akin to:

     dumb_array& operator=(const dumb_array& other)
     {
         if (this != &other) // (1)
         {
             // get the new data ready before we replace the old
             std::size_t newSize = other.mSize;
             int* newArray = newSize ? new int[newSize]() : nullptr; // (3)
             std::copy(other.mArray, other.mArray + newSize, newArray); // (3)
    
             // replace the old data (all are non-throwing)
             delete [] mArray;
             mSize = newSize;
             mArray = newArray;
         }
    
         return *this;
     }
    
  3. The code has expanded! Which leads us to the third problem: code duplication.

Our assignment operator effectively duplicates all the code we've already written elsewhere, and that's a terrible thing.

In our case, the core of it is only two lines (the allocation and the copy), but with more complex resources this code bloat can be quite a hassle. We should strive to never repeat ourselves.

(One might wonder: if this much code is needed to manage one resource correctly, what if my class manages more than one?
While this may seem to be a valid concern, and indeed it requires non-trivial try/catch clauses, this is a non-issue.
That's because a class should manage one resource only!)

A successful solution

As mentioned, the copy-and-swap idiom will fix all these issues. But right now, we have all the requirements except one: a swap function. While The Rule of Three successfully entails the existence of our copy-constructor, assignment operator, and destructor, it should really be called "The Big Three and A Half": any time your class manages a resource it also makes sense to provide a swap function.

We need to add swap functionality to our class, and we do that as follows†:

class dumb_array
{
public:
    // ...

    friend void swap(dumb_array& first, dumb_array& second) // nothrow
    {
        // enable ADL (not necessary in our case, but good practice)
        using std::swap;

        // by swapping the members of two objects,
        // the two objects are effectively swapped
        swap(first.mSize, second.mSize);
        swap(first.mArray, second.mArray);
    }

    // ...
};

(Here is the explanation why public friend swap.) Now not only can we swap our dumb_array's, but swaps in general can be more efficient; it merely swaps pointers and sizes, rather than allocating and copying entire arrays. Aside from this bonus in functionality and efficiency, we are now ready to implement the copy-and-swap idiom.

Without further ado, our assignment operator is:

dumb_array& operator=(dumb_array other) // (1)
{
    swap(*this, other); // (2)

    return *this;
}

And that's it! With one fell swoop, all three problems are elegantly tackled at once.

Why does it work?

We first notice an important choice: the parameter argument is taken by-value. While one could just as easily do the following (and indeed, many naive implementations of the idiom do):

dumb_array& operator=(const dumb_array& other)
{
    dumb_array temp(other);
    swap(*this, temp);

    return *this;
}

We lose an important optimization opportunity. Not only that, but this choice is critical in C++11, which is discussed later. (On a general note, a remarkably useful guideline is as follows: if you're going to make a copy of something in a function, let the compiler do it in the parameter list.‡)

Either way, this method of obtaining our resource is the key to eliminating code duplication: we get to use the code from the copy-constructor to make the copy, and never need to repeat any bit of it. Now that the copy is made, we are ready to swap.

Observe that upon entering the function that all the new data is already allocated, copied, and ready to be used. This is what gives us a strong exception guarantee for free: we won't even enter the function if construction of the copy fails, and it's therefore not possible to alter the state of *this. (What we did manually before for a strong exception guarantee, the compiler is doing for us now; how kind.)

At this point we are home-free, because swap is non-throwing. We swap our current data with the copied data, safely altering our state, and the old data gets put into the temporary. The old data is then released when the function returns. (Where upon the parameter's scope ends and its destructor is called.)

Because the idiom repeats no code, we cannot introduce bugs within the operator. Note that this means we are rid of the need for a self-assignment check, allowing a single uniform implementation of operator=. (Additionally, we no longer have a performance penalty on non-self-assignments.)

And that is the copy-and-swap idiom.

What about C++11?

The next version of C++, C++11, makes one very important change to how we manage resources: the Rule of Three is now The Rule of Four (and a half). Why? Because not only do we need to be able to copy-construct our resource, we need to move-construct it as well.

Luckily for us, this is easy:

class dumb_array
{
public:
    // ...

    // move constructor
    dumb_array(dumb_array&& other) noexcept ††
        : dumb_array() // initialize via default constructor, C++11 only
    {
        swap(*this, other);
    }

    // ...
};

What's going on here? Recall the goal of move-construction: to take the resources from another instance of the class, leaving it in a state guaranteed to be assignable and destructible.

So what we've done is simple: initialize via the default constructor (a C++11 feature), then swap with other; we know a default constructed instance of our class can safely be assigned and destructed, so we know other will be able to do the same, after swapping.

(Note that some compilers do not support constructor delegation; in this case, we have to manually default construct the class. This is an unfortunate but luckily trivial task.)

Why does that work?

That is the only change we need to make to our class, so why does it work? Remember the ever-important decision we made to make the parameter a value and not a reference:

dumb_array& operator=(dumb_array other); // (1)

Now, if other is being initialized with an rvalue, it will be move-constructed. Perfect. In the same way C++03 let us re-use our copy-constructor functionality by taking the argument by-value, C++11 will automatically pick the move-constructor when appropriate as well. (And, of course, as mentioned in previously linked article, the copying/moving of the value may simply be elided altogether.)

And so concludes the copy-and-swap idiom.


Footnotes

*Why do we set mArray to null? Because if any further code in the operator throws, the destructor of dumb_array might be called; and if that happens without setting it to null, we attempt to delete memory that's already been deleted! We avoid this by setting it to null, as deleting null is a no-operation.

†There are other claims that we should specialize std::swap for our type, provide an in-class swap along-side a free-function swap, etc. But this is all unnecessary: any proper use of swap will be through an unqualified call, and our function will be found through ADL. One function will do.

‡The reason is simple: once you have the resource to yourself, you may swap and/or move it (C++11) anywhere it needs to be. And by making the copy in the parameter list, you maximize optimization.

††The move constructor should generally be noexcept, otherwise some code (e.g. std::vector resizing logic) will use the copy constructor even when a move would make sense. Of course, only mark it noexcept if the code inside doesn't throw exceptions.

Jack Lilhammers
  • 1,084
  • 4
  • 16
GManNickG
  • 459,504
  • 50
  • 465
  • 534
  • @GMan: A custom overload of swap is still potentially faster, because move semantics has to set source pointers to zero after copying them. – fredoverflow Jul 19 '10 at 09:13
  • 1
    @Fred: An optimizing compiler can easily see that such an assignment is wasteful, though. I demonstrate that in my answer I linked to in the post. You may have a point, though, that without such things a `swap` might still be faster. I wouldn't consider it worth it anymore, though. – GManNickG Jul 19 '10 at 09:14
  • @GMan: If we rely on move semantics, the class definitely needs a move cctor and a move assignment operator. So this actually has more requirements than doing your own `swap()`. (I'm not arguing against doing so, just that this should be mentioned.) – sbi Jul 19 '10 at 09:25
  • @sbi: It is, "As long as one implements move-semantics.". :) Unless I'm misunderstanding. (EDIT: Guess not. :P) I'll make the C++0x section more concrete, though. By the way, I think the dtor requirement is made explicit when I say "this is for classes that implement The Big Three." I'll see if I can sneak it it. – GManNickG Jul 19 '10 at 09:32
  • 19
    @GMan: I would argue that a class managing several resources at once is doomed to fail (exception safety becomes nightmarish) and I would strongly recommend that either a class manages ONE resource OR it has business functionality and use managers. – Matthieu M. Jul 19 '10 at 11:53
  • @GMan nice one :) I wonder why you do that test against a zero size, though? [Dynamic Zero size arrays are OK](http://herbsutter.com/2009/09/02/when-is-a-zero-length-array-okay/). – Johannes Schaub - litb Jul 19 '10 at 19:17
  • @Johannes: I know zero-size dynamic arrays are okay, but I did the check to 1) avoid having to get any dynamic memory and 2) make it look more complex. :P And good to know it is sufficient, I'll admit I was a bit confused what he was talking about. – GManNickG Jul 19 '10 at 19:42
  • @Matthieu: I agree, I've added that point. @Johannes: I've touched it up a bit in regards to self-assignment. – GManNickG Jul 19 '10 at 21:15
  • 2
    Very nice summary! I'd personally comment out `throw()`. Leave the text there to indicate you don't think the function will throw, but leave off the potential penalties: http://www.boost.org/development/requirements.html#Exception-specification – Bill Aug 26 '10 at 16:35
  • 3
    @GMan: or you use the C++0x `nothrow` qualifier :-) – Matthieu M. Sep 21 '10 at 07:09
  • @Chubsdad: Thanks. Good timing, I was planning to (and just did) improve and expand on C++0x changes. – GManNickG Sep 29 '10 at 07:23
  • 2
    Big four? (1) dtor, (2) copy ctor, (3) copy op=, (4) move ctor, and (5) move op=. Which of those doesn't get counted? – James McNellis Oct 01 '10 at 20:33
  • @James: There's only one assignment operator. – GManNickG Oct 01 '10 at 20:38
  • @GMan: You can declare both a copy op= and a move op=, can't you? (The implicit move op= is suppressed if a copy op= is implemented, though). Or, that's my understanding. Why am I wrong? – James McNellis Oct 01 '10 at 22:11
  • 4
    @James: No, you're right, you can. You just take it by value in both C++03 and C++0x. (In C++03, lvalues get copied, rvalues hopefully get their copy elided, in C++0x lvalues get copied, ravlues get moved, and sometimes hopefully elided). – GManNickG Oct 01 '10 at 22:14
  • 3
    @GMan: just reread this article (C++0x part), and I was wondering about the *Big Four*. I would have said *Big Five* to include the *Move Assignment Operator* :P Is a *Move Assignment Operator* defined automatically if you don't declare one ? – Matthieu M. Nov 16 '10 at 13:42
  • @GMan: [C++0x will support implicit move, though with some restrictions](http://twitter.com/#!/sdt_intel/status/3224317093482496). (We'll have to wait until the next mailing at the end of the month to learn what restrictions were agreed upon...) – James McNellis Nov 17 '10 at 07:32
  • @GMan, thanks for the very good tutorial! do you mind in giving an example how does a non-overloaded `copy` method would work using the copy-and-swap idiom, for instance, a method like `void copy(const dumb_array& other)` ? – Javier Jun 15 '11 at 07:53
  • @Javier: I'm not sure I understand what you mean. Maybe you can ask a full question if needed, be sure to include code examples. – GManNickG Jun 15 '11 at 08:58
  • @GMan, would the method below be conceptually correct following the copy-and-swap idiom? `void dumb_array::copy(dumb_array other) { swap(*this, other); }` – Javier Jun 15 '11 at 09:24
  • @Javier: The idiom is useful for implementing the necessarily functions for copy-semantics (the big three). I guess my answer is "yes", but I can't see a use for such a function. – GManNickG Jun 15 '11 at 09:24
  • 1
    @Javier: I can't think of a case where I only need to copy certain members. Perhaps those members should be their own class with their own functionality. – GManNickG Jun 15 '11 at 19:11
  • 1
    The subject that one would have to spend hours reading all over the web is now put into a Single Compact Complete and Perfect (c) explanation. How kind @GMan! +1 – jweyrich Jun 27 '11 at 16:23
  • What if dumb_array's constructor is `explicit` ? Calling `operator=(dumb_array other)` won't work, will it? – Gabriel Nov 03 '11 at 00:58
  • @Gabriel: Calling it with what? If it were, say, `dumb_array x(1); x = 5;`, then no, it wouldn't work. But that's really nothing to do with anything except the explicit constructor. – GManNickG Nov 03 '11 at 02:38
  • @GMan Ok, then it means that if a class needs its constructor to be explicit for any reason, it can't use this implementation of the copy and swap idiom. A slight change in `operator=` would fix this: passing the object to be copied by reference and declaring a local object at the beginning of the function, copy-constructing it. – Gabriel Nov 03 '11 at 08:55
  • 1
    @Gabriel: I'm not sure I understand. `explicit` constructors and copy-and-swap are two totally unrelated things. Changing the parameter to a reference still wouldn't allow the code in my previous example to work. – GManNickG Nov 03 '11 at 19:03
  • @GMan Sorry I was talking about explicit *copy*-constructor. The way you put it, if you need to set `dumb_array`'s copy-constructor as explicit, you won't be able to write: `dumb_array a, b; a = b;`. Change the copy-constructor to `dumb_array& operator=(dumb_array & other) { swap(*this, dumb_array(other)); return *this; }` and now you can. – Gabriel Nov 04 '11 at 11:02
  • 2
    @Gabriel: Ah, indeed. That said, I've never found a solid justification for making a copy-constructor explicit. – GManNickG Nov 04 '11 at 11:06
  • 25
    I don't get why swap method is declared as friend here? – szx Dec 13 '11 at 06:10
  • 12
    @asd: To allow it to be found through ADL. – GManNickG Dec 13 '11 at 06:11
  • And I don't get why the default constructor does "new int[mSize]()" while the copy constructor does "new int[mSize]". What's the difference? – neuviemeporte Jul 19 '12 at 14:26
  • 9
    @neuviemeporte: With the parenthesis, the arrays elements are default initialized. Without, they are uninitialized. Since in the copy constructor we'll be overwriting the values anyway, we can skip initialization. – GManNickG Jul 19 '12 at 15:01
  • @GMan: Thanks. Also, I was wondering if it would be possible for swap() to be a member function of dumb_array. Global friend functions seem messy to me. – neuviemeporte Jul 19 '12 at 16:40
  • 3
    @neuviemeporte: If you want it to be found during ADL (`using std::swap; swap(x, y);`), it needs to be a global friend. – GManNickG Jul 19 '12 at 17:44
  • 2
    @GMan: I'm not sure I understand; wouldn't making swap() a member make it simpler so you don't have to care about ADL at all? – neuviemeporte Jul 19 '12 at 21:21
  • 10
    @neuviemeporte: You need your `swap` to be found during ADL if you want it to work in most generic code that you'll come across, like `boost::swap` and other various swap instances. Swap is a tricky issue in C++, and generally we've all come to agree that a single point of access is best (for consistency), and the only way to do that in general is a free function (`int` cannot have a swap member, for example). See [my question](http://stackoverflow.com/questions/9170247/does-c11-change-the-behavior-of-explicitly-calling-stdswap-to-ensure-adl-loc) for some background. – GManNickG Jul 19 '12 at 22:25
  • How do you do `swap` for a derived class? – Kerrek SB Sep 07 '12 at 08:59
  • 1
    @KerrekSB: Sounds like a reasonable question on it's own. I'll admit I'm not sure I fully understand the use-case you're after though. – GManNickG Sep 07 '12 at 16:10
  • @GManNickG: The immediate motivation is [this problem](http://stackoverflow.com/a/12315771/596781), but I've recently been wondering in general if one can write a swap for a derived class by slicing, provided the base provides a swap. – Kerrek SB Sep 07 '12 at 16:20
  • @KerrekSB: I'd just take the approach you're taking, swapping the base classes before swapping the most derived class. – GManNickG Sep 07 '12 at 18:56
  • @GManNickG: I've come to the conclusion that slicing exists precisely for the purpose of implementing copy constructor, copy assignment and swap functions for derived classes. Swap was added just today as this dawned on me. Does that sound reasonable? – Kerrek SB Sep 07 '12 at 19:01
  • @KerrekSB: Well, I don't actually see any slicing. It's my understanding that `A::swap(rhs)` is the same as `this->A::swap(rhs)`; if `A::swap` called any virtual functions, for example (it shouldn't), they could be dispatched to the most derived class. – GManNickG Sep 07 '12 at 19:04
  • @GManNickG: Hm, good point; calling the base `swap` doesn't actually slice. But assuming that each class implements its own "standard" swap semantics, it "slice-swaps" only the base part of the object. – Kerrek SB Sep 07 '12 at 19:09
  • @GManNickG Your move constructor depends on the default ctor. Does that mean that (since move semantics) it's obligatory for every resource wrapper to have 1) a valid "empty" state and 2) a default ctor that initializes to this state? – Kos Dec 23 '12 at 17:26
  • 1
    @Kos: Technically no. You can do whatever you want in your move constructor and have whatever moved-from state. But it's more than good practice to reset to a simple reusable state. The standard library classes (like `std::vector<>`) do such a thing. – GManNickG Dec 23 '12 at 17:51
  • 1
    @GManNickG In a previous comment you say that `swap` should be a global friend to in order for it to be found through ADL. In your example, `swap` looks like a public member of `dumb_array`. What am I missing? – zmb Jun 12 '13 at 14:40
  • 2
    @zmb: The "global" part (that is, being declared completely outside the class) is optional. What's important is that it's a non-member friend function, so that it will be found during ADL, such as when the standard library calls `swap(x, y);`. See [this](http://stackoverflow.com/questions/5695548/public-friend-swap-member-function) for more complete information. – GManNickG Jun 12 '13 at 15:10
  • 3
    Would the recommendation about `dumb_array(dumb_array&& other)` (i.e. default construct then swap) be different if `dumb_array::dumb_array()` was expensive (say, it always reserved some space)? Would it be better in that case to initialise members by moving from the other object, then null out the moved-from object's state? – Ben Hymers Jul 29 '13 at 21:43
  • 7
    @BenHymers: Yes. The copy-and-swap idiom is only intended to simplify the creation of new resource managing classes in a general way. For every particular class, there is almost certainly a more efficient route. This idiom is just something that works and is hard to do wrong. – GManNickG Jul 30 '13 at 01:47
  • @GManNickG your answer is helpful, but some credit to [http://www.icce.rug.nl/documents/cplusplus/cplusplus11.html#l194] [http://www.icce.rug.nl/documents/cplusplus/cplusplus09.html#l159], Scott Meyers wouldn't harm – Nikolaos Giotis Sep 29 '13 at 18:25
  • @GManNickG: FWIW, statements like dumb_array temp(other); are untenable in generic code (e.g. an STL vector implementation). The reason is that the size of dumb_array is arbitrary and may be too large for the stack and crash the application due to stack space exhaustion. I've run into this situation a number of times in practice, especially on platforms with small default stacks like Mac OS X. – ThreeBit Nov 24 '13 at 00:11
  • 1
    @ThreeBit: The array is not stored in stack space, as is also the case in `std::vector` with the default allocator. You have run into a different problem in practice. – GManNickG Nov 24 '13 at 03:40
  • @GManNickG You're right about vector as pretty much all vector implementations allocate their memory on the heap. I shouldn't have used that as a supposed example. I meant that in general you can't write generic code that creates instances of arbitrary objects on the stack. There are alternatives, of course. – ThreeBit Nov 25 '13 at 06:04
  • Mine is an unusual case, but I encountered a subtle problem using std::swap on a class that has a pointer into a string that's part of the object. Discussed here: http://stackoverflow.com/a/20513948/522385 – Chap Dec 12 '13 at 02:12
  • 2
    Why is this the swap function declared with as a `friend` at this line `friend void swap(dumb_array& first, dumb_array& second) // nothrow`? if its declared within a class isnt it just a member function? – Vis Viva Apr 08 '14 at 12:51
  • 3
    @VisViva a friend in a class is a non member function in the surrounding scope, but as a friend it gets the usual access to non-public members. It's a stylistic choice, but can be nicely concise to define small functions there, rather than declare them and have to repeat the signature (possibly with more explicit template parameters) in the surrounding scope before launching into the function body. – Tony Delroy Apr 15 '14 at 23:54
  • @TonyD but why is it defined in the class itself? shouldnt it be written somewhere else and instead just be declared inside a class with a `friend` keyword? – Vis Viva Apr 16 '14 at 06:05
  • @VisViva that's only a style issue - concision and clear participation in the class's overall interface vs separation of interface from implementation (and potentially avoiding being implicitly inline) - pick as you like. – Tony Delroy Apr 16 '14 at 10:18
  • 3
    Implementing the assignment via the copy constructor may cause an unneeded allocation, potentially even causing an uncalled for out-of-memory error. Consider the case of assigning a 700MB `dumb_array` to a 1GB `dumb_aaray` on a machine with a <2GB heap limit. An optimal assignment will realize it already has enough memory allocated and only copy the data into it's already allocated buffer. Your implementation will cause an allocation of another 700MB buffer before the 1GB one is released, causing all 3 to try co-exist in memory at once, which will throw an out-of-memory error needlessly. – Baruch May 27 '14 at 13:33
  • 1
    As is called out multiple times, the point is to have something that works, not something that is optimal. There is a trade-off between engineering time and application time. – GManNickG May 30 '14 at 19:55
  • 3
    @GManNickG: Since it sacrifices efficiency for maintainability, you should probably explicitly point out somewhere in either the question or the answer that copy-and-swap is **a** solution, not **the** solution, and that it may make sense to re-use the object rather than construct a new one. Otherwise people will have no idea. (It seems obvious but it's not; it took me a long time to realize this.) – user541686 Aug 08 '14 at 04:50
  • For anyone wondering why to use unqualified swaps: std::swap will generally call a copy constructor (move constructor in C++11+) and two assignment operators, and for classes using the copy-and-swap idiom, each assignment operator will call the class-specific swap function anyway. Therefore, using std::swap would cost twice as much, PLUS the cost of a move or copy constructor. That's not to say copy-and-swap makes std::swap super-slow; std::swap is just suboptimal. – Mike S Aug 21 '14 at 05:29
  • On tradeoffs: I benchmarked a class like the one above using three versions: An old-style operator with a self-assignment test, a copy-and-swap, and a slightly more manual "copy-and-move" which only swaps resources and move-assigns everything else. For an empty array, copy-and-swap is about 25% faster than the old-style assignment with Clang, but for non-empty arrays or for GCC, the old-style assignment is ~0-12.5% faster. "Copy-and-move" generally shaves a tiny fraction off copy-and-swap times, but it doesn't doesn't benefit from whatever optimization Clang used above. (cont.) – Mike S Aug 21 '14 at 05:41
  • I had hoped avoiding the self-assignment test would pay for copy-and-swap's extra operations, but it seems that's not always true (probably due to fast branch prediction). Still, it's close and sometimes wins depending on circumstances, so you don't have to pay much for formulaic correctness and exception safety, at least for this particular class. However, according to Howard Hinnant, the idiom can be up to 8x slower for std::vector, so YMMV. – Mike S Aug 21 '14 at 18:47
  • 3
    @GManNickG why aren't you making it a "Big five" with a move assignment operator? or is it no longer needed with the given implementation of the assignment operator? It's not clear from neither the comments nor the text, if you could please add a paragraph about it that would be great! thanks :) – user1708860 Oct 18 '14 at 12:10
  • This answer is made of Win and oozing of Pure Awesomeness. I lost count long-ago how many times I've referenced it from other answers or comments. – WhozCraig Jan 11 '15 at 22:01
  • 2
    This post is old now and maybe someone already mentioned it (too many posts to review), but the technique you're using in your move constructor will cause some to wince. Delegating to the default constructor and then swapping is inefficient and unnecessary, since you should just directly move "other" into the object being created. It's the most natural way to do it (really the de facto way IMHO). – Larry Apr 21 '15 at 18:12
  • 1
    On an unrelated note, caution advised in the constructors of your very first code example. The initialization of "mArray" using "mSize" would be in trouble if someone ever switched the declaration order of "msize" and "mArray" (since "mArray" would then be initialized before "mSize" so the latter would contain garbage when "new int[mSize]" is invoked). – Larry Apr 21 '15 at 18:15
  • Great article ! I spotted one mistake though. In C++11, an rvalue reference parameter *is still an lvalue inside the function*. You just have the information that it can be moved from, but you need to do it explicitly. See [this live example](http://coliru.stacked-crooked.com/a/58918bb253c60971). Thanks for your time :) – Quentin May 06 '15 at 21:28
  • There's one thing I don't understand about this. Why is it necessary to swap with the parameter to the copy constructor and `operator =()`, instead of just copying by reference/moving members from it into `*this`? Isn't it a waste to modify the parameter, since it's just temporary? – Andy Jun 29 '15 at 18:11
  • 1
    An important aspect of move assignments is that they should be `noexcept`. Is it safe to say that the *unified* `operator=(dumb_array other)` (which is a move and a copy assignment in one) is `noexcept`? – becko Jul 21 '15 at 19:09
  • I wonder if copy and swap has any advantage over copy and move. – MikeMB Jul 27 '15 at 23:43
  • @Andy and MikeMB - I'm learning from this post, but my understanding (correct me if I'm wrong GManNickG) is if you just copy or move to destination, any pointers in destination (target) object are now a memory leak; any objects in target would not get destructed either, would they? By swapping, you essentially Destruct the old target, giving it a chance to "clean up" on the way out. – Luv2code Jul 30 '15 at 16:41
  • Why provide a `std::swap` friend void function? Why not just used `std::swap` twice when it's needed? I didn't understand this point – FreelanceConsultant Aug 25 '15 at 22:49
  • Regarding std::swap, if you have an object in your class that has a specialized swap, I'm assuming you'd prefer that, correct? e.g.: prefer `str.swap(str2)` to `std::swap(str,str2)` and prefer `myvector.swap(yourvector)` to `std::swap(myvector,yourvector)`. Is that correct? Or would you still prefer std::swap even in those cases where a specialized swap exists? – Luv2code Sep 23 '15 at 12:46
  • 1
    @Luv2code For both `std:.string` and `std::vector` there is a specialization of `std::swap` that does exactly that. So there is no need to call the specialized one. Hopefully all your classes will also have their own free `swap()` function so you don't need to call the member ones. – rozina Oct 09 '15 at 07:20
  • 1
    Oh, interesting @rozina . Rather than use a class specialization explicitly, you use `swap(a,b)` (swap **without std scoping** is critical part of this, right?) and overloading/Argument-Dependent Lookup (ADL) will resolve the specialized method. Of course, `using std::swap` at the top of your class's friend `swap` method basically provides the fall-back/safety net if a specialized method is not required or provided. Is my new understanding (recapped here) correct? Is that the recommended "best practice"? – Luv2code Oct 10 '15 at 13:23
  • 1
    @Luv2code: Since vector and other standard library types live in the `std::` namespace, in their specific case it doesn't matter if you do `std::swap` or `swap` with ADL, as the result is the same. The `std::swap` function for `std::vector` just calls `vec.swap(othervec)` anyway. In general, prefer `using std::swap` followed by unqualified `swap` (or even better, `boost::swap`, which does this for you). – GManNickG Oct 10 '15 at 20:41
  • Are you sure that `std::swap()` uses the assignment operator in its implementation? Stroustrup says it doesn't (*The C++ Programming Language (4th ed.), §17.5.1, pg. 508*) – Paolo M Nov 11 '15 at 13:53
  • Furthermore, saying that a `swap function is a non-throwing function` is incorrect. In fact, `std::swap(T&, T&)` does throw if the copy constructor of `T` throws. – Paolo M Nov 11 '15 at 14:03
  • @GManNick If `this->size==other.size` (or when copying objects like `std::vector` if `this->capacity>=other.size()`), then your implementation of the copy assignment requires an unnecessary allocation and de-allocation. Btw, `std::vector` is *not* using this idom for its copy and move assignments -- there must be a reason. – Walter Nov 20 '15 at 23:08
  • 1
    @Walter: Copy-and-swap isn't *the* way to do copies or assignment, it's *a* way. And its purpose is to be simple and correct, not efficient or specialized. – GManNickG Nov 21 '15 at 23:07
  • So should my assignment operator pass by reference or by value? The question is not clear – FreelanceConsultant Apr 07 '16 at 23:29
  • 2
    In the interests of balance there are [Drawbacks of Implementing Move Assignment in Terms of Swap](https://scottmeyers.blogspot.co.uk/2014/06/the-drawbacks-of-implementing-move.html). It's probably worth mentioning some of what's discussed there, so that programmers understand what trade-offs are being made. In most circumstances, I find it a good bargain, as I can quickly get correct, safe code; then, if copy-and-swap isn't efficient enough, it can be replaced later. – Toby Speight Dec 16 '16 at 09:09
  • `::std::swap` arguments are move-constructable and move-assignable in C++11 [utility.swap] (or `::std::swap` falls back to the provided custom swap implementation) So, I guess, `::std::swap` can be used to implement copy assignment. – Sergey.quixoticaxis.Ivanov Jul 13 '17 at 12:09
  • 2
    Could you please update the answer to how the move ctor and move operator= would be implemented with copy-and-swap idiom? The C++11 section is outdated (rule of 4?), and current resources on the matter (IMHO) are unclear and deviate greatly. When trying to only change the operator= there's an overload ambiguity https://stackoverflow.com/questions/29698747/can-i-write-both-copy-and-move-assignment-operators-for-a-class – blz Jan 20 '18 at 15:31
  • does the following work? `operator=(const A & a0) {A a1(a0); return &a1;}`. It is very simple, so maybe I am missing something – Nisba Apr 14 '18 at 16:52
  • 1
    @Nisba: No. `a1` doesn't exist once the function returns. Returning a reference or pointer to it will inevitably result in undefined behavior. – GManNickG Apr 14 '18 at 19:35
  • 1
    Note that this means we are rid of the need for a self-assignment check, allowing a single uniform implementation of operator=. (Additionally, we no longer have a performance penalty on non-self-assignments.) How is this implementation beneficial for self-assignments? – Sai Kumar Battinoju Mar 15 '19 at 07:30
  • 1
    Thoughts a *swapless* implementation like this? ```dumb_array& operator=(dumb_array other) { this->~dumb_array(); return *new(this)(other); }``` I *think* it solves all the same problems as the copy-and-swap idiom, without relying on various unintuitions (e.g. the necessity that ```swap``` is a friend and called unqualified). To understand the code, the reader must know that dtors and ctors are functions that can be called (the ctor via placement new); but everything it does is explicit and more difficult to misunderstand. – Apriori Jan 16 '20 at 03:26
  • Implementing the swap idiom means you have to call swap EVERY field in your object manually. So if a developer adds a new field but forgets adding swap for it. Subtle bugs result. – Calmarius Mar 27 '20 at 18:34
  • What is the condition in "mArray(mSize ? new int[mSize] : nullptr)" ? What is the purpose of checking that mSize returns true or false? – Fox42 Nov 27 '20 at 13:33
296

Assignment, at its heart, is two steps: tearing down the object's old state and building its new state as a copy of some other object's state.

Basically, that's what the destructor and the copy constructor do, so the first idea would be to delegate the work to them. However, since destruction mustn't fail, while construction might, we actually want to do it the other way around: first perform the constructive part and, if that succeeded, then do the destructive part. The copy-and-swap idiom is a way to do just that: It first calls a class' copy constructor to create a temporary object, then swaps its data with the temporary's, and then lets the temporary's destructor destroy the old state.
Since swap() is supposed to never fail, the only part which might fail is the copy-construction. That is performed first, and if it fails, nothing will be changed in the targeted object.

In its refined form, copy-and-swap is implemented by having the copy performed by initializing the (non-reference) parameter of the assignment operator:

T& operator=(T tmp)
{
    this->swap(tmp);
    return *this;
}
sbi
  • 204,536
  • 44
  • 236
  • 426
  • 1
    I think that mentioning the pimpl is as important as mentioning the copy, the swap and the destruction. The swap isn't magically exception-safe. It's exception-safe because swapping pointers is exception-safe. You don't **have** to use a pimpl, but if you don't then you must make sure that each swap of a member is exception-safe. That can be a nightmare when these members can change and it is trivial when they're hidden behind a pimpl. And then, then comes the cost of the pimpl. Which leads us to the conclusion that often exception-safety bears a cost in performance. – wilhelmtell Dec 22 '10 at 14:41
  • ... you can write allocators for the class which will maintain the pimpl's cost amortized. That adds complexity, which hits on the simplicity of the plain-vanilla copy-and-swap idiom. It's a choice. – wilhelmtell Dec 22 '10 at 14:46
  • 7
    `std::swap(this_string, that)` doesn't provide a no-throw guarantee. It provides strong exception safety, but not a no-throw guarantee. – wilhelmtell Dec 22 '10 at 14:59
  • This means that if you have two members of class-type then in the assignment-operator if the first swap worked and the second failed then you'll have to make sure you undo the first swap to maintain the strong-exception safety. And this is with just two members of class-type. – wilhelmtell Dec 22 '10 at 15:01
  • @wilhelmtell: I doubt that `std::swap()` violates the no-throw guarantee when instantiated with `std::string`. (That `+1` on your comment was me failing on a mouse click.) There is a specialization for `std::string` calling `std::string::swap()`. [C++03, 21.3.7.8: "Effect: `lhs.swap(rhs);`"] – sbi Dec 22 '10 at 15:10
  • 11
    @wilhelmtell: In C++03, there is no mention of exceptions potentially thrown by `std::string::swap` (which is called by `std::swap`). In C++0x, `std::string::swap` is `noexcept` and must not throw exceptions. – James McNellis Dec 22 '10 at 15:24
  • 2
    @sbi @JamesMcNellis ok, but the point still stands: if you have members of class-type you must make sure swapping them is a no-throw. If you have a single member that is a pointer then that's trivial. Otherwise it isn't. – wilhelmtell Dec 22 '10 at 15:34
  • @wilhelmtell: Yes. I'm surprised that the new C++0x Swappable concept does not mandate user-defined `swap` functions be `noexcept`. – James McNellis Dec 22 '10 at 15:43
  • 2
    @wilhelmtell: I thought that was the point of swapping: it never throws and it is always O(1) (yeah, I know, `std::array`...) – sbi Dec 22 '10 at 15:50
  • @sbi, what if we would like to use `copy method` that is similar in functionality to the `operator =`. For instance, a method like `void copy(const T & other)`. How does the copy-and-swap idiom could be used in this case? – Javier Jun 15 '11 at 08:59
  • @Javier: What would that method do? – sbi Jun 15 '11 at 10:02
  • @sbi, in the case of overloading the `operator=` we copy the whole class members. In my case, I need to copy only certain class members & I wanted to use the `copy-swap` protocol to do that, e.g. `void myClass::copy(myclass tmp){using std::swap; swap(member1,tmp.member1); swap(member3,tmp.member3); };`. Does this make sense? What about the `tmp` parameter, should it be passed as value? – Javier Jun 15 '11 at 10:09
  • @Javier: Yes, then copying (by taking the argument by value) and swapping (by swapping individual members) makes sense. Well, insofar as having a method named `copy()` that only copies part of an object makes sense at all. (I'd at least name it `copy_from()` or something similar, so that it is clearer what the direction is.) – sbi Jun 15 '11 at 10:18
  • @sbi, great! Thanks for the explanations! But, I'm not really sure that I understood why does the argument should be by value? – Javier Jun 15 '11 at 10:32
  • @Javier: Because you need to make a copy anyway. (You don't want to swap with the original one, but with a copy of it.) You could just as well pass by `const` reference and then make a copy of the argument within the function. However, the idiomatic way to do this is to take the argument by copy instead. – sbi Jun 15 '11 at 11:35
  • Is it safe to mark `T& operator=(T tmp)` as `noexcept`? – becko Jul 21 '15 at 19:10
  • @becko: Not in general, no. It invokes a copy ctor, and generally these can allocate resources, which might fail, and result in exceptions thrown. – sbi Jul 22 '15 at 08:19
  • @sbi I think I agree with Daniel Frey's answer here: http://stackoverflow.com/a/18848460/855050 (which I found later). If you have a counter argument please share it with me. – becko Jul 22 '15 at 13:01
  • @becko: He does make a very good point, so I retract my statement and insist that the opposite is true. `:)` – sbi Jul 22 '15 at 14:21
47

There are some good answers already. I'll focus mainly on what I think they lack - an explanation of the "cons" with the copy-and-swap idiom....

What is the copy-and-swap idiom?

A way of implementing the assignment operator in terms of a swap function:

X& operator=(X rhs)
{
    swap(rhs);
    return *this;
}

The fundamental idea is that:

  • the most error-prone part of assigning to an object is ensuring any resources the new state needs are acquired (e.g. memory, descriptors)

  • that acquisition can be attempted before modifying the current state of the object (i.e. *this) if a copy of the new value is made, which is why rhs is accepted by value (i.e. copied) rather than by reference

  • swapping the state of the local copy rhs and *this is usually relatively easy to do without potential failure/exceptions, given the local copy doesn't need any particular state afterwards (just needs state fit for the destructor to run, much as for an object being moved from in >= C++11)

When should it be used? (Which problems does it solve [/create]?)

  • When you want the assigned-to objected unaffected by an assignment that throws an exception, assuming you have or can write a swap with strong exception guarantee, and ideally one that can't fail/throw..†

  • When you want a clean, easy to understand, robust way to define the assignment operator in terms of (simpler) copy constructor, swap and destructor functions.

    • Self-assignment done as a copy-and-swap avoids oft-overlooked edge cases.‡

  • When any performance penalty or momentarily higher resource usage created by having an extra temporary object during the assignment is not important to your application. ⁂

swap throwing: it's generally possible to reliably swap data members that the objects track by pointer, but non-pointer data members that don't have a throw-free swap, or for which swapping has to be implemented as X tmp = lhs; lhs = rhs; rhs = tmp; and copy-construction or assignment may throw, still have the potential to fail leaving some data members swapped and others not. This potential applies even to C++03 std::string's as James comments on another answer:

@wilhelmtell: In C++03, there is no mention of exceptions potentially thrown by std::string::swap (which is called by std::swap). In C++0x, std::string::swap is noexcept and must not throw exceptions. – James McNellis Dec 22 '10 at 15:24


‡ assignment operator implementation that seems sane when assigning from a distinct object can easily fail for self-assignment. While it might seem unimaginable that client code would even attempt self-assignment, it can happen relatively easily during algo operations on containers, with x = f(x); code where f is (perhaps only for some #ifdef branches) a macro ala #define f(x) x or a function returning a reference to x, or even (likely inefficient but concise) code like x = c1 ? x * 2 : c2 ? x / 2 : x;). For example:

struct X
{
    T* p_;
    size_t size_;
    X& operator=(const X& rhs)
    {
        delete[] p_;  // OUCH!
        p_ = new T[size_ = rhs.size_];
        std::copy(p_, rhs.p_, rhs.p_ + rhs.size_);
    }
    ...
};

On self-assignment, the above code delete's x.p_;, points p_ at a newly allocated heap region, then attempts to read the uninitialised data therein (Undefined Behaviour), if that doesn't do anything too weird, copy attempts a self-assignment to every just-destructed 'T'!


⁂ The copy-and-swap idiom can introduce inefficiencies or limitations due to the use of an extra temporary (when the operator's parameter is copy-constructed):

struct Client
{
    IP_Address ip_address_;
    int socket_;
    X(const X& rhs)
      : ip_address_(rhs.ip_address_), socket_(connect(rhs.ip_address_))
    { }
};

Here, a hand-written Client::operator= might check if *this is already connected to the same server as rhs (perhaps sending a "reset" code if useful), whereas the copy-and-swap approach would invoke the copy-constructor which would likely be written to open a distinct socket connection then close the original one. Not only could that mean a remote network interaction instead of a simple in-process variable copy, it could run afoul of client or server limits on socket resources or connections. (Of course this class has a pretty horrid interface, but that's another matter ;-P).

Tony Delroy
  • 94,554
  • 11
  • 158
  • 229
  • 4
    That said, a socket connection was just an example - the same principle applies to any potentially expensive initialisation, such as hardware probing/initialisation/calibration, generating a pool of threads or random numbers, certain cryptography tasks, caches, file system scans, database connections etc.. – Tony Delroy Oct 21 '14 at 05:09
  • There is one more (massive) con. As of current specs *technically* the object will *not have an move-assignment operator!* If later used as member of a class, the new class *will not have move-ctor auto-generated!* Source: http://youtu.be/mYrbivnruYw?t=43m14s – user362515 Feb 14 '15 at 12:55
  • 3
    The main problem with the copy assignment operator of `Client` is that assignment is not forbidden. – sbi Jul 22 '15 at 14:27
  • In the client example, the class should be made noncopyable. – John Z. Li May 03 '19 at 07:25
25

This answer is more like an addition and a slight modification to the answers above.

In some versions of Visual Studio (and possibly other compilers) there is a bug that is really annoying and doesn't make sense. So if you declare/define your swap function like this:

friend void swap(A& first, A& second) {

    std::swap(first.size, second.size);
    std::swap(first.arr, second.arr);

}

... the compiler will yell at you when you call the swap function:

enter image description here

This has something to do with a friend function being called and this object being passed as a parameter.


A way around this is to not use friend keyword and redefine the swap function:

void swap(A& other) {

    std::swap(size, other.size);
    std::swap(arr, other.arr);

}

This time, you can just call swap and pass in other, thus making the compiler happy:

enter image description here


After all, you don't need to use a friend function to swap 2 objects. It makes just as much sense to make swap a member function that has one other object as a parameter.

You already have access to this object, so passing it in as a parameter is technically redundant.

Oleksiy
  • 30,852
  • 19
  • 65
  • 114
  • 1
    @GManNickG https://www.dropbox.com/s/o1mitwcpxmawcot/example.cpp https://www.dropbox.com/s/jrjrn5dh1zez5vy/Untitled.jpg. This is a simplified version. A error seems to occur every time a `friend` function is called with `*this` parameter – Oleksiy Sep 04 '13 at 05:20
  • 1
    @GManNickG as I said, it's a bug and might work fine for other people. I just wanted to help some people who might have the same problem as me. I tried this with both Visual Studio 2012 Express and 2013 Preview and the only thing that made it go away, was my modification – Oleksiy Sep 04 '13 at 05:23
  • Yeah, definitely a bug. This should probably be a comment on an existing answer, though, and not an answer, since it doesn't answer the actual question. People may -1 it. – GManNickG Sep 04 '13 at 05:27
  • 8
    @GManNickG it wouldn't fit in a comment with all the images and code examples. And it's ok if people downvote, I'm sure there's someone out there who's getting the same bug; the information in this post might be just what they need. – Oleksiy Sep 04 '13 at 05:33
  • 14
    note that this is only a bug in the IDE code highlighting (IntelliSense)... It will compile just fine with no warnings/errors. – Amro Oct 11 '13 at 07:29
  • 3
    Please report the VS bug here if you have not done so already (and if it has not been fixed) https://connect.microsoft.com/VisualStudio – Matt May 13 '14 at 17:27
  • 1
    A year after this post this error still hasn't been fixed?! – simonides Dec 01 '14 at 13:27
  • 1
    I understand that the motivation for this approach might be just to workaround the IDE, but you gave a reasonable argument about redundancy in defining a `friend` function. Why isn't this the default implementation approach? Is that just a matter of C++ philosophy or just by chance the `friend` one became the most common? Is it a common scenario that someone else except the class itself is going to call `swap`? – villasv Nov 12 '15 at 16:22
  • 1
    @VillasV see http://stackoverflow.com/questions/5695548/public-friend-swap-member-function – Mark Ransom Nov 21 '16 at 21:26
18

I would like to add a word of warning when you are dealing with C++11-style allocator-aware containers. Swapping and assignment have subtly different semantics.

For concreteness, let us consider a container std::vector<T, A>, where A is some stateful allocator type, and we'll compare the following functions:

void fs(std::vector<T, A> & a, std::vector<T, A> & b)
{ 
    a.swap(b);
    b.clear(); // not important what you do with b
}

void fm(std::vector<T, A> & a, std::vector<T, A> & b)
{
    a = std::move(b);
}

The purpose of both functions fs and fm is to give a the state that b had initially. However, there is a hidden question: What happens if a.get_allocator() != b.get_allocator()? The answer is: It depends. Let's write AT = std::allocator_traits<A>.

  • If AT::propagate_on_container_move_assignment is std::true_type, then fm reassigns the allocator of a with the value of b.get_allocator(), otherwise it does not, and a continues to use its original allocator. In that case, the data elements need to be swapped individually, since the storage of a and b is not compatible.

  • If AT::propagate_on_container_swap is std::true_type, then fs swaps both data and allocators in the expected fashion.

  • If AT::propagate_on_container_swap is std::false_type, then we need a dynamic check.

    • If a.get_allocator() == b.get_allocator(), then the two containers use compatible storage, and swapping proceeds in the usual fashion.
    • However, if a.get_allocator() != b.get_allocator(), the program has undefined behaviour (cf. [container.requirements.general/8].

The upshot is that swapping has become a non-trivial operation in C++11 as soon as your container starts supporting stateful allocators. That's a somewhat "advanced use case", but it's not entirely unlikely, since move optimizations usually only become interesting once your class manages a resource, and memory is one of the most popular resources.

Kerrek SB
  • 428,875
  • 83
  • 813
  • 1,025