1
votes

I understand the need for deep copies, and to ensure my code behaves as desired, I am writing a copy ctor, assignment operator, and dtor for my class.

However, it seems to me that in every case, an assignment operator has to do first what the destructor does (deallocate any dynamically allocated memory, to prevent memory leaks), and then what the copy constructor does (make deep copies of all dynamically allocated data and copy those into the instance being constructed).

Is there a case where an assignment operator should conceptually do something other than the following?

class SomeClass{
    //member data, copy ctor, dtor
    SomeClass& operator=(SomeClass const& rhs){
        //what the destructor does
        //what the copy constructor does
    }
};

The code is practically identical, and seems like a waste of time to rewrite. Other than invoking the destructor directly at the beginning of the assignment operator, I can't think of a way to reuse the copy constructor code I've already written, since I believe doing something like

*this=rhs

would only recursively invoke the assignment operator, since techinically, "this" has already been constructed.

1
I think this should probably answer all your questions: What is copy-and-swap?Barry
It's also briefly covered in the C++-faq's section on self-assignment. See also Rule of Three. To echo @JohnDibling's comment, see Rule of Zero.user3920237
Ideally, in a well-designed class, you won't need a constructor, copy-assignment operator or destructor at all. Unless you need a virtual destructor, but even then it should be trivial.John Dibling
Well who knew that vector and unique_ptr were such poorly designed classes!Barry
I'm not saying Rule of Zero is bad or wrong. I'm just saying it's not universally correct, and that the suggestion that failing to follow it is indicative of bad design is ridiculous.Barry

1 Answers

1
votes

As mentioned in the comments, your concerns about code duplication is addressed by applying the copy-and-swap idiom:

class SomeClass{
    //member data, copy ctor, dtor
    SomeClass& operator=(SomeClass rhs){
        swap(*this, rhs);
        return *this;
    }
    friend void swap(SomeClass& first, SomeClass& second) {
        using std::swap;     
        swap(first.data, second.data);
        //... etc. for other data members
    }
};

You have to implement the additional swap function, but your copy constructor and destructor code is reused in a natural way. The copy constructor is used when the source of the assignment is passed to the assignment operator, and the destructor is reused when the argument is destructed when the assignment operator returns.