Solved

Polymorphism is not working with the copy-assignment operator!

Posted on 2001-08-16
21
291 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
Is Your AD Toolbox Looking More Like a Toybox?

Managing Active Directory can get complicated.  Often, the native tools for managing AD are just not up to the task.  The largest Active Directory installations in the world have relied on one tool to manage their day-to-day administration tasks: Hyena. Start your trial today.

 
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
 
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

What is SQL Server and how does it work?

The purpose of this paper is to provide you background on SQL Server. It’s your self-study guide for learning fundamentals. It includes both the history of SQL and its technical basics. Concepts and definitions will form the solid foundation of your future DBA expertise.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Suggested Solutions

Title # Comments Views Activity
Grammars for C C++ and java 1 122
Angular JS Route 3 71
Error creating a new C++ project in ,net 20 36
JavaFX TableView not displaying correctly 3 18
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…
IntroductionThis article is the second in a three part article series on the Visual Studio 2008 Debugger.  It provides tips in setting and using breakpoints. If not familiar with this debugger, you can find a basic introduction in the EE article loc…
The goal of the tutorial is to teach the user how to use functions in C++. The video will cover how to define functions, how to call functions and how to create functions prototypes. Microsoft Visual C++ 2010 Express will be used as a text editor an…
The viewer will be introduced to the technique of using vectors in C++. The video will cover how to define a vector, store values in the vector and retrieve data from the values stored in the vector.

777 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