C ++ is a safe / standard approach for a derived class to "inherit" an overloaded assignment operator from a base class

In my opinion, in C ++, derived classes do not inherit overloaded assignment operators from base classes. I wrote an example below where I explicitly overload the assignment operator for both the base class and the derived class. In the sample output below is a section:

Index:1 - Base Value:1
Index:1 - Derived Value:2
Index:2 - Base Value:2
Index:2 - Derived Value:2

      

The expected output should be:

Index:1 - Base Value:2
Index:1 - Derived Value:2
Index:2 - Base Value:2
Index:2 - Derived Value:2 

      

This conclusion was not unexpected. I understand that the assignment operator is not inherited. Is there a de facto safe / standard approach for a derived class to invoke the base class assignment operator? So far, my guess is that the derived derived class operator translates the derived class object to the base class object and assigns the RHS to the LHS. Is this a generally accepted approach?

If I use the "Suggested Solution" (at the bottom of the page) it works, but only because none of the operators are declared as virtual

. If I use a non virtual function and use a non virtual function then the version used depends on what the role is, and if I use a virtual function then the member function depends on the actual type of the object rather than throwing how.

Thank.

Code Listing


/*******************************************************************************
 * Preprocessor Directives
 ******************************************************************************/
#include <iostream>
using namespace std;


/*******************************************************************************
 * Class Declarations and Function Prototypes
 ******************************************************************************/
class Base {
   private:
   protected:
   public:
      int iBInt;
      Base();        /* Constructor */
      Base(int a);   /* Constructor - Set const member */
      Base & operator=(const Base& rhs);
      virtual ~Base();
};

class Derived : public Base {
   private:
   protected:
   public:
      int iDInt;
      Derived();        /* Constructor */
      Derived(int a);   /* Constructor - Set const member */
      Derived & operator=(const Derived& rhs);
      ~Derived();
};

/*******************************************************************************
 * Class and Function Definitions
 ******************************************************************************/
/******************************************************************************/
Base::Base(void) {
   cout << __FUNCTION__ << endl;
   iBInt = 0;
   cout << "iBInt: " << iBInt << endl;
}

/******************************************************************************/
Base::Base(int a) {
   cout << __FUNCTION__ << endl;
   iBInt = a;
   cout << "iBInt: " << iBInt << endl;
}

/******************************************************************************/
Base::~Base(void) {
   cout << __FUNCTION__ << endl;
}

/******************************************************************************/
Base& Base::operator=(const Base& rhs) {
   cout << "Base::" << __FUNCTION__ << endl;
   if (this == &rhs) {
      return *this;
   }
   iBInt = rhs.iBInt;
   cout << "iBInt: " << iBInt << endl;
   return *this;
}

/******************************************************************************/
Derived::Derived(void) {
   cout << __FUNCTION__ << endl;
   iDInt = 0;
   cout << "iDInt: " << iDInt << endl;
}

/******************************************************************************/
Derived::Derived(int a) : Base(a) {
   cout << __FUNCTION__ << endl;
   iDInt = a;
   cout << "iDInt: " << iDInt << endl;
}

/******************************************************************************/
Derived::~Derived(void) {
   cout << __FUNCTION__ << endl;
}

/******************************************************************************/
Derived& Derived::operator=(const Derived& rhs) {
   cout << "Derived::" << __FUNCTION__ << endl;
   if (this == &rhs) {
      return *this;
   }
   iDInt = rhs.iDInt;
   cout << "iDInt: " << iDInt << endl;
   return *this;
}


/*******************************************************************************
 * Main Entry Point
 ******************************************************************************/
int main(void) {
   int count = 3;
   /* Generate objects */
   Derived **bArr = new Derived*[count];
   for (int i=0; i<count; i++) {
      bArr[i] = new Derived(i);
   }

   /* Set some values via overloaded assignment operator, and print out
    * updated values.
    */
   for (int i=0; i<count; i++) {
      cout << "Index:" << i << " - Base Value:" << bArr[i]->iBInt << endl;
      cout << "Index:" << i << " - Derived Value:" << bArr[i]->iDInt << endl;
   }
   *bArr[1] = *bArr[2];
   for (int i=0; i<count; i++) {
      cout << "Index:" << i << " - Base Value:" << bArr[i]->iBInt << endl;
      cout << "Index:" << i << " - Derived Value:" << bArr[i]->iDInt << endl;
   }
   /* Cleanup */
   for (int i=0; i<count; i++) {
      delete bArr[i];
   }
   delete [] bArr;

   return 0;
}

      

Output result


Base
iBInt: 0
Derived
iDInt: 0
Base
iBInt: 1
Derived
iDInt: 1
Base
iBInt: 2
Derived
iDInt: 2
Index:0 - Base Value:0
Index:0 - Derived Value:0
Index:1 - Base Value:1
Index:1 - Derived Value:1
Index:2 - Base Value:2
Index:2 - Derived Value:2
Derived::operator=
iDInt: 2
Index:0 - Base Value:0
Index:0 - Derived Value:0
Index:1 - Base Value:1
Index:1 - Derived Value:2
Index:2 - Base Value:2
Index:2 - Derived Value:2
~Derived
~Base
~Derived
~Base
~Derived
~Base

      

Suggested solution / Editing


/******************************************************************************/
Derived& Derived::operator=(const Derived& rhs) {
   cout << "Derived::" << __FUNCTION__ << endl;
   if (this == &rhs) {
      return *this;
   }
   Base* b1 = this;
   Base* b2 = (Base*)&rhs;
   *b1 = *b2;
   iDInt = rhs.iDInt;
   cout << "iDInt: " << iDInt << endl;
   return *this;
}

      

+3


source to share


2 answers


I would just call the assignment operator Base

directly:

Base::operator =(rhs);

      

Calling the assignment operator of an inherited class in this way is much cleaner and more direct than using it with pointer gymnastics.




An alternative way to do it:

static_cast<Base &>(*this) = rhs;

      

This is cleaner than calling the operator using pointers, but still (IMO) less readable than explicit base operator redirection.

+7


source


Is there an actual safe / standard approach for creating a derived class to call the base class assignment operator?

Assignment operators (and operators in general) almost always imply value semantics . It makes sense to assign to string, vector, map, date, or int.

Virtual functions almost always imply the exact opposite. It is impractical to assign a stream, window, or database implementation. Almost any class that uses virtual functions should disable copying and copying.

All the problems you experience are caused by the obvious desire to mix things that are difficult to mix, and which usually shouldn't be mixed at all.

I would advise you to turn off copying in the base class and rename the assignment operator to a regular member function:



class Base : boost::noncopyable { // in C++11, use `= delete`
   public:
      int iBInt;
      Base();        /* Constructor */
      Base(int a);   /* Constructor - Set const member */
      virtual ~Base();
      virtual void Assign(Base const &other);
};

      

You will almost certainly find a more descriptive name than Assign

that, and most likely you will most likely find that it would be completely wrong to call the Assign function in the first place.

In fact, now that the function has a normal name, you can reevaluate the entire design of your class without being "abstract" by the operator syntax.

Possible problems:

  • In object oriented programming, it is generally considered bad style to override a function just to call the parent implementation, because that means the base class does not sufficiently set the invariant.

  • Taking a base class argument in a base class virtual function can lead to very tricky projects as you are actually trying to make the function virtual with respect to two arguments. Both are "invisible" this

    and other

    can be of any class in the hierarchy. Maybe for any of the four combinations Base

    and Derived

    need to do four different things? And what if (what to expect) even more derived classes come into play? And is object-oriented programming just here to sprinkle similar implementations through source code, not centralize them in one place?

+2


source







All Articles