Go Premium for a chance to win a PS4. Enter to Win

x
  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 336
  • Last Modified:

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?
0
Mensana
Asked:
Mensana
  • 10
  • 10
1 Solution
 
nietodCommented:
The standard operator = can't work polymorphically so there  is no point in making it virtual.  Note the signatures

 virtual Base_Class& operator=(Base_Class &rhs);
 Derived_Class& operator=(Derived_Class &rhs);

The derived version does not override the base class version.  it OVERLOADS it.  It takes a different parameter type, so it is an overload, not an override.

continues
0
 
nietodCommented:
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
0
 
nietodCommented:
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.
0
Independent Software Vendors: We Want Your Opinion

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
nietodCommented:
Let me know if you have any questions.
0
 
MensanaAuthor Commented:
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?
0
 
MensanaAuthor Commented:
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.
0
 
MensanaAuthor Commented:
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.
0
 
nietodCommented:
>> 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
0
 
nietodCommented:
>> 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?
0
 
MensanaAuthor Commented:
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.
0
 
nietodCommented:
>> 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]
0
 
MensanaAuthor Commented:
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.
0
 
nietodCommented:
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.
0
 
nietodCommented:
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.
0
 
MensanaAuthor Commented:
>>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!
0
 
MensanaAuthor Commented:
That's what I did! Sorry for that!
0
 
nietodCommented:
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.
0
 
MensanaAuthor Commented:
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?
0
 
MensanaAuthor Commented:
Thank you very much for such a thorough explanation. I still have lots to learn.
0
 
jasonclarkeCommented:
Scott Meyers talks about potential problems of virtual assignment operators in More Effective C++, Item 33:  'Make non-leaf classes abstract'.
0
 
MensanaAuthor Commented:
Thank you Jason. I spent my week-end studying this problem and I found that item too.
Thanks again,
Eddie
0

Featured Post

What does it mean to be "Always On"?

Is your cloud always on? With an Always On cloud you won't have to worry about downtime for maintenance or software application code updates, ensuring that your bottom line isn't affected.

  • 10
  • 10
Tackle projects and never again get stuck behind a technical roadblock.
Join Now