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?
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=()
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
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
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.
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?
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?
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.
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::VirtualAssi gn(Base_Cl ass &rhs);
{
// Do derived work here.
Base_Class::VirtualAssign( rhs); // Now do base class work.
}
continues
>> 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::VirtualAssi
{
// Do derived work here.
Base_Class::VirtualAssign(
}
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?
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?
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]
>> 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]
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.
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::VirtualAssi gn(Base_Cl ass &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!
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::VirtualAssi
{
// Do derived work here.
Base_Class::VirtualAssign(
}
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!
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.
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.
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?
*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?
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'.
ASKER
Thank you Jason. I spent my week-end studying this problem and I found that item too.
Thanks again,
Eddie
Thanks again,
Eddie
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