Link to home
Start Free TrialLog in
Avatar of Mensana
Mensana

asked on

Polymorphism is not working with the copy-assignment operator!

Hi there,
I have a problem with a small piece of code because it does not work as I would expect. I have 2 classes:

class Base_Class
{
   // some functions and some data members

   public:
   virtual Base_Class& operator=(Base_Class &rhs);

   // some other functions and data members
}

class Derived_Class : Base_Class
{
   // some functions and some data members

public:
   Derived_Class& operator=(Derived_Class &rhs);

   // some other functions and data members
}

I also have a STL list declared like this:

std::list<Base_Class *> _lstDerivedObjects

that is populated with pointers to objects of type Derived_Class.

I declared an iterator like this:

std::list<Base_Class *>::iterator pIterator = _lstDerivedObjects.begin();

I have an object of type Derived_Class and I want to assigned to it an object from the list, so I have something like this:

Base_Class *pDerivedObject = new Derived_Class();

// . . .

*pDerivedObject = **pIterator;

For some reasons, instead of calling the Derived_Class::operator=(), the framework calls the Base_Class::operator=().
I have to mention that the code that does that is located at the Base_Class level and I want to keep it that way. The Base_Class will be inherited in a multitude of other classes and I want to rely on the polymorphic mechanism to call the right function. Therefore, I don?t want to cast the pointers and force this way the operator in the Derived_Class to be called.
I guess that the signature for the overriding copy-assignment operator is changed by the different type of its return value and input parameter, although this should not be the case, since I have references and not values.
Anybody any clue?
ASKER CERTIFIED SOLUTION
Avatar of nietod
nietod

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of nietod
nietod

One solution to this is to provide a 2nd function that is virtual and that does the polymorphic work and then neve the base class operator = (which doesn't need to be virtual) call this virtual function.  For example

class Base_Class
{
   // some functions and some data members

   public:
   Base_Class& operator=(Base_Class &rhs)
   {
       VirtualAssign(rhs);
       return *this;
   }

   virtual void VirtualAssign(Base_Class &rhs);
};

// By the way you didn't have a public derivation from
// the base class in your example.  I assume that was
// a mistake in your question example, nto your
// actual code.  right?
class Derived_Class : public Base_Class
{
public:
   // No need to define opeator = since the base class
   // will call VirtualAssign().

   virtual void VirtualAssign(Base_Class &rhs);
   {
      // Do work here.  
      // Note that the parameter is of
      // type Base_Class.
   }
};

continues
Now note something about this solution.  (Which is a fundamental issue in what you are trying to do.)  The virtual assign function takes a parameter of type Base_class, not Derived_Class.   If this is a problem, you you may need to perform a dynamic_cast to convert the parameter to the Derived_Class type.  However, you aren't really assured that the cast will be successful.  If the VirtualAssign() is used only from operator =, then the cast will always succeed, but if the function is used from another source, this cast could fail depending on what is passed to it.
Let me know if you have any questions.
Avatar of Mensana

ASKER

The derivation is indeed public. My project is a whole lot bigger and the classes are named differently. I wrote this piece of code just to describe the nature of the error I encountered.
As per the proposed virtual function, I would suspect that it should call also the version implemented in the parent class, since I have data members that are at different levels.
Just a question: This non-polymorphic thing goes for operator== and operator!= as well?
Avatar of Mensana

ASKER

This non-polymorphic issue is really interesting and it bothers me, because although I read C++ books and articles (Scott Meyers, Lippmann, Herb Sutter, Al Stevens, you name it) all the time, I don't recolect reading about such a basic notion that should be learned when you are still a beginner.
Avatar of Mensana

ASKER

This non-polymorphic issue is really interesting and it bothers me, because although I read C++ books and articles (Scott Meyers, Lippmann, Herb Sutter, Al Stevens, you name it) all the time, I don't recolect reading about such a basic notion that should be learned when you are still a beginner.
>> My project is a whole lot bigger and the
>> classes are named differently.
I guess as much.  There aren't a lot of "actual" classes called base_class.

>> I would suspect that it should call also the
>> version implemented in the parent class, since
>> I have data members that are at different levels.
I'm not 100% sure what you are saying.  But if I had to guess you are saying that the base class version needs to do some work and the derived class version needs to do some additional work, but the base class's work still needs to be done.  If that is the case, the derived class version can call the base class version (non-virutally) to have the  base class work done.  like

virtual void Derived_Class::VirtualAssign(Base_Class &rhs);
{
   // Do derived work here.  
   Base_Class::VirtualAssign(rhs); // Now do base class work.
}

continues
>> since I have data members that are at different levels.
This statement makes me a little nervious.

Usually you need to overload operator = for classes that have data members that need very special handling.  Its somewhat unusall to have these data members in more than once class in a hierachy.  (There are pretty rare to begin with.)  It makes me wonder if you are making this polymorphic by mistake.

What is your goal here?  Why does this need to act polymorphically?
Avatar of Mensana

ASKER

This non-polymorphic issue is really interesting and it bothers me, because although I read C++ books and articles (Scott Meyers, Lippmann, Herb Sutter, Al Stevens, you name it) all the time, I don't recolect reading about such a basic notion that should be learned when you are still a beginner.
>> I don't recolect reading about such a
>> basic notion that should be learned
>> when you are still a beginner.
I am 90% sure that Meyers covers in in Effective C++ or its sequal.

But here is the quote from the C++ standard



13.5.3  Assignment                                          [over.ass]

1 An  assignment  operator  shall  be implemented by a non-static member
  function with exactly one parameter.  Because a copy assignment opera-
  tor  operator=  is  implicitly declared for a class if not declared by
  the user (_class.copy_), a base class assignment  operator  is  always
  hidden by the copy assignment operator of the derived class.

2 Any  assignment  operator,  even  the copy assignment operator, can be
  virtual.  [Note: for a derived class D with a base class B for which a
  virtual  copy assignment has been declared, the copy assignment opera-
  tor in D does not  override  B's  virtual  copy  assignment  operator.
  [Example:
          struct B {
                  virtual int operator= (int);
                  virtual B& operator= (const B&);
          };
          struct D : B {
                  virtual int operator= (int);
                  virtual D& operator= (const B&);
          };
          D dobj1;
          D dobj2;
          B* bptr = &dobj1;
          void f() {
                  bptr->operator=(99);    // calls D::operator(int)
                  *bptr = 99;             // ditto
                  bptr->operator=(dobj2); // calls D::operator(const B&)
                  *bptr = dobj2;          // ditto
                  dobj1 = dobj2;          // calls implicitly-declared
                                          // D::operator(const D&)
          }
   --end example]  --end note]
Avatar of Mensana

ASKER

This non-polymorphic issue is really interesting and it bothers me, because although I read C++ books and articles (Scott Meyers, Lippmann, Herb Sutter, Al Stevens, you name it) all the time, I don't recolect reading about such a basic notion that should be learned when you are still a beginner.
That might be a little hard to understand.  The point is that non-copy assignment operators, like the one that takes an int as a parameter, will act virtually.  But copy assignment operators, like the pair that takes "const B &" as a parameter, do not act polymorphically.
If you are usign "refresh" ("reload") to get back to this page, the "refresh" command performs whatever action it took to get the page generated the previous time.  Those actions might include posting a comment.  That is probably why your comments are reappearing again and again.  You can  use the link on the EE notification e-mail to get to the question without reloading.
Avatar of Mensana

ASKER

>>I'm not 100% sure what you are saying.  But if I had to guess you are saying that the base class version
needs to do some work and the derived class version needs to do some additional work, but the base class's
work still needs to be done.  If that is the case, the derived class version can call the base class
version (non-virutally) to have the  base class work done.  like

virtual void Derived_Class::VirtualAssign(Base_Class &rhs);
{
  // Do derived work here.  
  Base_Class::VirtualAssign(rhs); // Now do base class work.
}


That?s exactly what I had in mind.


>> since I have data members that are at different levels.
This statement makes me a little nervious.

Usually you need to overload operator = for classes that have data members that need very special handling.
 Its somewhat unusall to have these data members in more than once class in a hierachy.  (There are
pretty rare to begin with.)  It makes me wonder if you are making this polymorphic by mistake.

What is your goal here?  Why does this need to act polymorphically?


I should give here a very long explanation of my whole design. Just to resume things, I will say that I have ?members that need very special handling? at two levels (a parent class and its child).

I came back with a previous question: this thing is also valid for operator== and operator!=?

I will give you the points for a very good answer!
Avatar of Mensana

ASKER

That's what I did! Sorry for that!
Back up a minute and thing about what you are asking for.

I beleive that you want to support only

Base op Base
Derived op Derived

where op is =, ==, !- etc.

But this sort of approach alows for

Derived op Base

Like in

Base *D = new Derived;
Base *B = new Base;

*D op *B;

You need to consider what you want to do in this case. There is no way to prevent it.  You either need to make it work logically or you need to make it detect the error condition and throw an exception or something.
Avatar of Mensana

ASKER

No, I want to compare only objects of same type, so I should not allow the compilation of something like
*D op *B; But that's done because B is a pure virtual class and cannot be instantiated;
On the other hand, when comparing
Base *pD1 = new Derived(), *pD2 = new Derived();
if( *pD1 == *pD2 ) { /* ... */ }
I want to compare data members at every level, respectively.
Do I make sense?
Avatar of Mensana

ASKER

Thank you very much for such a thorough explanation. I still have lots to learn.
Scott Meyers talks about potential problems of virtual assignment operators in More Effective C++, Item 33:  'Make non-leaf classes abstract'.
Avatar of Mensana

ASKER

Thank you Jason. I spent my week-end studying this problem and I found that item too.
Thanks again,
Eddie