On Monday, June 22, 2015 at 10:01:57 AM UTC+2, Paul wrote:
> On Sunday, June 21, 2015 at 9:05:21 PM UTC+1, Paul N wrote:
> > On Sunday, 21 June 2015 17:02:09 UTC+1, Paul wrote:
> > > The code below is copy-pasted from a university website. I don't understand the need for void make_empty() which is called in the copy constructor.
>
> Thanks. Sorry, I meant assignment, not the copy constructor.
When a copy assignment operator is called, the list is already initialized and may contain nodes. These have to be removed. This is work that the copy constructor doesn't have to do.
Conversely, when the copy constructor is invoked there are no nodes to be removed, and the list is not yet initialized, so the copy constructor has to initialize various members. This is work that the copy assignment operator doesn't have to do.
Copy assignment: clear first.
Copy construction: initialize first.
Still, there is no need to do that removal/clearing by duplicating code...
The basic approach for a copy assignment operator is to express it in terms of copy construction, destruction and a no-throwing swap (like std::swap):
class List
{
...
void swap_with( List& other ) noexcept
{
// Implementation of no-throwing swap
// For example, std::swap( head, other.head ), etc.
}
auto operator=( List const& other )
-> List&
{
List the_copy( other ); // Invokes copy constructor
swap_with( the_copy );
return *this; // Invokes destructor for old list
}
With this idiomatic approach code is not duplicated, just reused, and any exception occurring during the copying will leave the list in its original good state.
Some people prefer to pass the argument by value, which means that the copy constructor is invoked at the point of call rather than in the assignment operator implementation itself, but at first sight it may be a little baffling:
auto operator=( List the_copy )
-> List&
{
swap_with( the_copy );
return *this; // Invokes destructor for old list
}
Notes about the notation above:
* Instead of a "swap_with" method, it's my impression that most people prefer a non-member "swap" function, perhaps expressed as an inline "friend" function. However, a non-member "swap" function can easily be expressed in terms of a "swap_with" method. The opposite, expressing a member function in terms of non-member function, is a bit unnatural to me.
* Instead of the C++11 "auto" syntax for functions (a.k.a. trailing return type), most people still prefer the old C++03 function declaration syntax. I see no reason to use the old syntax except inertia, since the new syntax covers it all. The new syntax e.g. allows using class-local types in the return type without qualification, and it makes it easier for me to spot the function name.
Cheers & hth.,
- Alf