1

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.

Jack Knudson
  • 51
  • 1
  • 2
  • 10
  • 3
    I think this should probably answer all your questions: [What is copy-and-swap?](http://stackoverflow.com/questions/3279543/what-is-the-copy-and-swap-idiom) – Barry Nov 07 '14 at 23:35
  • It's also briefly covered in the [C++-faq](http://www.parashift.com/c++-faq/assignment-operators.html)'s section on self-assignment. See also [Rule of Three](https://stackoverflow.com/questions/4172722/what-is-the-rule-of-three). To echo @JohnDibling's comment, see [Rule of Zero](http://flamingdangerzone.com/cxx11/2012/08/15/rule-of-zero.html). –  Nov 07 '14 at 23:37
  • 1
    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 Nov 07 '14 at 23:38
  • Well who knew that `vector` and `unique_ptr` were such poorly designed classes! – Barry Nov 07 '14 at 23:40
  • @Barry That's not what he's saying at all. See the edit in my above comment for a possible interpretation (i.e., prefer not to have to deal with resource management at all.) –  Nov 07 '14 at 23:40
  • @Barry: Way to be open-minded to new thoughts. – John Dibling Nov 07 '14 at 23:40
  • @remyabel: Your interpretation is spot-on. – John Dibling Nov 07 '14 at 23:42
  • "Well-designed" and "need a copy assignment operator" are not related concepts. You can't hand-wave away resource management because "use vector!" and "use unique_ptr!" are not the answer to literally every problem ever. *somebody* has to deal with it, and those classes are presumably well-designed. Your statement isn't a new thought, it's a massive over-generalization. – Barry Nov 07 '14 at 23:47
  • @barry Have you even taken a look at the link? It shows an example of a class that eliminates the special member functions by simply providing a `std::unique_ptr` data member. That's the whole point. No need to reinvent the wheel. No duplication, **which is exactly what the OP wants.** –  Nov 07 '14 at 23:50
  • @remyabel I did read it. Notice that it actually caveats on ownership, whereas John does not. – Barry Nov 07 '14 at 23:53
  • 2
    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 Nov 07 '14 at 23:57
  • Barry is correct. It is true that as long as the developer is willing and able to use the standard containers and use standard smart pointers (which may require providing custom deleters) everywhere in the code, Ro0 is achievable. However, interoperability issues other software components may make it much more challenging, and self managing the memory may lead to a cleaner design choice. – jxh Nov 08 '14 at 03:01
  • @Barry: You inferred that suggestion. I didn't say that the rule of zero is an absolute. – John Dibling Nov 08 '14 at 05:17

1 Answers1

1

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.

Community
  • 1
  • 1
jxh
  • 64,506
  • 7
  • 96
  • 165