Solved

Polymorphism is not working with the copy-assignment operator!

Posted on 2001-08-16
21
285 Views
Last Modified: 2013-12-14
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
Comment
Question by:Mensana
  • 10
  • 10
21 Comments
 
LVL 22

Accepted Solution

by:
nietod earned 200 total points
ID: 6393232
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
 
LVL 22

Expert Comment

by:nietod
ID: 6393264
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
 
LVL 22

Expert Comment

by:nietod
ID: 6393304
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
 
LVL 22

Expert Comment

by:nietod
ID: 6393312
Let me know if you have any questions.
0
 
LVL 1

Author Comment

by:Mensana
ID: 6393317
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
 
LVL 1

Author Comment

by:Mensana
ID: 6393339
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
 
LVL 1

Author Comment

by:Mensana
ID: 6393344
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
 
LVL 22

Expert Comment

by:nietod
ID: 6393345
>> 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
 
LVL 22

Expert Comment

by:nietod
ID: 6393358
>> 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
 
LVL 1

Author Comment

by:Mensana
ID: 6393377
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
What Is Threat Intelligence?

Threat intelligence is often discussed, but rarely understood. Starting with a precise definition, along with clear business goals, is essential.

 
LVL 22

Expert Comment

by:nietod
ID: 6393399
>> 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
 
LVL 1

Author Comment

by:Mensana
ID: 6393410
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
 
LVL 22

Expert Comment

by:nietod
ID: 6393435
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
 
LVL 22

Expert Comment

by:nietod
ID: 6393446
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
 
LVL 1

Author Comment

by:Mensana
ID: 6393448
>>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
 
LVL 1

Author Comment

by:Mensana
ID: 6393459
That's what I did! Sorry for that!
0
 
LVL 22

Expert Comment

by:nietod
ID: 6393505
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
 
LVL 1

Author Comment

by:Mensana
ID: 6393541
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
 
LVL 1

Author Comment

by:Mensana
ID: 6393610
Thank you very much for such a thorough explanation. I still have lots to learn.
0
 
LVL 9

Expert Comment

by:jasonclarke
ID: 6397661
Scott Meyers talks about potential problems of virtual assignment operators in More Effective C++, Item 33:  'Make non-leaf classes abstract'.
0
 
LVL 1

Author Comment

by:Mensana
ID: 6406526
Thank you Jason. I spent my week-end studying this problem and I found that item too.
Thanks again,
Eddie
0

Featured Post

Top 6 Sources for Identifying Threat Actor TTPs

Understanding your enemy is essential. These six sources will help you identify the most popular threat actor tactics, techniques, and procedures (TTPs).

Join & Write a Comment

Suggested Solutions

This article will show you some of the more useful Standard Template Library (STL) algorithms through the use of working examples.  You will learn about how these algorithms fit into the STL architecture, how they work with STL containers, and why t…
Introduction This article is a continuation of the C/C++ Visual Studio Express debugger series. Part 1 provided a quick start guide in using the debugger. Part 2 focused on additional topics in breakpoints. As your assignments become a little more …
THe viewer will learn how to use NetBeans IDE 8.0 for Windows to perform CRUD operations on a MySql database.
The viewer will be introduced to the member functions push_back and pop_back of the vector class. The video will teach the difference between the two as well as how to use each one along with its functionality.

758 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

19 Experts available now in Live!

Get 1:1 Help Now