More C++ Idioms/Copy-and-swap
From Wikibooks, the open-content textbooks collection
Contents |
[edit]
Copy-and-swap
[edit] Intent
To create an exception safe implementation of overloaded assignment operator.
[edit] Also Known As
Create-Temporary-and-Swap
[edit] Motivation
Exception safety is a very important corner stone of highly reliable C++ software that uses exceptions to indicate "exceptional" conditions. There are at least 3 types of exception safety levels: Basic, Strong, and exception neutrality. Basic exception safety should be offered always as it is usually cheap to implement. Guaranteeing strong exception safety may not be possible in all the cases. Create a temporary and swap idiom elegantly implements a strongly exception safe assignment operator.
[edit] Solution and Sample Code
Create a temporary and swap idiom acquires new resource before it forfeits its current resource. To acquire the new resource, it uses RAII idiom. If the acquisition of the new resource is successful, it exchanges the resources using the non-throwing swap idiom. Finally, the old resource is released as a side effect of using RAII in the first step.
class String { public: String & operator = (String const &s) { String temp (s); // Copy-constructor -- RAII temp.swap (*this); // Non-throwing swap return *this; }// Old resources released when destructor of temp is called. void swap (String &s) throw (); /// See non-throwing swap idiom };
Some variations of the above implementation are also possible. A check for self assignment is not strictly necessary but can give some performance improvements.
class String { public: String & operator = (String const &s) { if (this != &s) String(s).swap (*this); // Copy-constructor and non-throwing swap // Old resources are released with the destruction of the temporary above return *this; } void swap (String &s) throw (); /// See non-throwing swap idiom };
copy elision and copy-and-swap idiom
Strictly speaking, explicit creation of a temporary inside the assignment operator is not necessary. The parameter (right hand side) of the assignment operator can be passed-by-value to the function. The parameter itself serves as a temporary.
String & operator = (String s) // the pass-by-value parameter serves as a temporary { s.swap (*this); // Non-throwing swap return *this; }// Old resources released when destructor of s is called.
This is not just a matter of convenience but in fact an optimization. If the parameter (s) binds to a lvalue (another non-const object), a copy of the object is made automatically while creating the parameter (s). However, when s binds to a rvalue (temporary object, literal), the copy is typically elided, which saves a call to a copy constructor and a destructor. In the earlier version of the assignment operator where the parameter is accepted as const reference, copy elision does not happen when the reference binds to a rvalue. This results into an additional object being created and destroyed. An example follows.
String createString(); // a function that returns a String object. String s; s = createString(); // right hand side is a rvalue. Pass-by-value style assignment operator // could be more efficient than pass-by-const-reference style assignment // operator.
Not every class benefits from this style of assignment operator. Consider a String assignment operator, which releases old memory and allocates new memory only if the existing memory is insufficient to hold a copy of the right hand side String object. To implement this optimization, one would have to write a custom assignment operator. Since a new String copy would nullify the memory allocation optimization, this custom assignment operator would have to avoid copying its argument to any temporary Strings, and in particular would need to accept its parameter by const reference.