0

I know that for freestanding classes, you should avoid calling the assignment operator in your copy constructor. Copy and swap and moving the reused code to a private member function are two ways to easily reuse code. However, recently I ran into a bit of a problem. Here's the code:

// Derived.h
class Derived : Base {
  // bunch of fun stuff here
  // ...
  // constructor that builds a derived object from a base one
  explicit Derived(const Base& base);
  // Assignment operator for moving base class member variables into the derived one
  Derived& operator=(const Base& base);
};
// Derived.cpp
Derived::Derived(const& Base base) {
  *this = base; // use assignment operator from derived to base
}
Derived& Derived::operator=(const Base& base) {
  static_cast<Base>(*this) = base;  // call base class assignment operator
}

In this given application, this all actually makes sense, because the derived class can now perform operations on the members it just received from the base class to fill in the rest of the object. Also, this provides a safe way for the user to convert a base object to a derived object. What I seem to be missing is whether or not such code is good code practice, or if there is an easier/better way to accomplish what I am trying to do? As I mentioned before, I know it's generally a no-go to call the assignment operator from a copy constructor in a freestanding class, but what about calling the assignment operator from another constructor?

It'sPete
  • 5,083
  • 8
  • 39
  • 72
  • Umm... Why should one avoid using an assignment operator in a copy constructor? – Beta Jun 26 '13 at 00:56
  • See: http://stackoverflow.com/questions/2639017/calling-assignment-operator-in-copy-constructor – It'sPete Jun 26 '13 at 00:59
  • 3
    What is the reason why you don't call the base class constructor from the derived class constructor, instead of going via assignment operators? – jogojapan Jun 26 '13 at 01:00
  • It seems like `Base` should be a member rather than a base.... – Billy ONeal Jun 26 '13 at 01:05
  • @BillyONeal, I can certainly see your point. However, given the implementation of base, there are certain encapsulation issues that need to be worked around. Namely, there are functions in base that are protected and need to be used in derived, but the user can't really have access to them, otherwise they have the opportunity to really do some damage to the object. Long story... – It'sPete Jun 26 '13 at 01:11

2 Answers2

8
Derived::Derived(const Base& base) {
  *this = base;
}

This default-constructs the Base subobject within the constructed Derived object and then assigns it. You might do better with:

Derived::Derived(const Base& base)
  : Base(base)
{
}

which uses the copy constructor of Base.

aschepler
  • 70,891
  • 9
  • 107
  • 161
  • Yea, I was looking for a way to call the copy constructor of base within the derived class, but within the function body, I'd get yelled at. Forgot about initializer lists (my lack of C++ syntax skills strikes again)! Thanks for the tip. Also, is there anything wrong or hacky about the assignment operator implmentation? Can I just call the base class's assignment operator like I'm doing? Thanks again! – It'sPete Jun 26 '13 at 01:02
  • 1
    @It'sPete To answer the question about your current implementation, yes, you can use a cast to trigger a call of the inherited `operator=`. But you'd have to cast to a reference, not a new object, i.e. use `static_cast(*this) = base;`, rather than `static_cast(*this) = base;`. The way you are doing it right now it'll create a new temporary object and assign the contents of `base` to that. – jogojapan Jun 26 '13 at 01:16
  • @jogojapan Thank you, that makes total sense... I come from C originally and am still getting away from C-style casts. – It'sPete Jun 26 '13 at 01:36
1

In constructor you should use initializer list:

Derived::Derived(const Base& base) : Base(base) {}

And if somewhere you actually wanted to reassign your base slice, you just need to call the already existing op= explicitly:

*this = Base::operator=(base);
Balog Pal
  • 16,195
  • 2
  • 23
  • 37
  • @BalogPal `*this = Base::operator=(base);` is equivalent to `this->operator=(this->Base::operator=(base));`. So yes, there are two assignments. – Casey Jun 26 '13 at 02:39