I know that the assignment operator is not inherited by derived classes, instead the compiler will create a default one if it is not redeclared. But I do not understand why the output of the following code snippet is Base operator=:
#include <iostream>
using namespace std;
class B {
protected:
int h;
public:
B& operator=(const B& ob){
if (this!=&ob) {
h = ob.h;
cout << "Base operator=\n";
}
return *this;
}
};
class D: public B {
protected:
float r;
public:
};
int main() {
D a, b;
a = b;
return 0;
}
Doesn't that mean that when calling a = b the base B& operator=(const B& ob, so isn't it inherited? Where am I wrong ?
The generated assignment is "all the base assignments, in order of inheritance declaration", so your generated assignment is essentially
D& operator=(const D& d)
{
B::operator=(d);
return *this;
}
If you were to derive from both B and C - in that order; class D: B, C - it would be equivalent to
D& operator=(const D& d)
{
B::operator=(d);
C::operator=(d);
return *this;
}
That is, the assignment is not inherited, but it's used.
With the expression a = b, the compiler generated assignment operator for D calls the user-defined assignment operator in B.
Yes you are correct that assignment operators are not inherited.
Related
i have a question about assigning one member of the derived class to another of the same derived class,
Can someone please help me understand why this code is printing 14 and not 34?
i dont understand when b2.nb is getting the value of b1.nb, because in this code:
A& operator=(const A& a){
na = a.na*2;
return *this;
}
there is no assignment of b1.nb to b2.nb
class A{
public:
A(int i){na = i;}
A& operator=(const A& a){
na = a.na*2;
return *this;
}
int na;
};
class B : public A{
public:
B(int i, int j): A(j){nb = i;}
int nb;
};
int main()
{
B b1(1,2);
B b2(3,4);
b2 = b1;
cout<<b2.nb<<b2.na;
}
From cppreference
Implicitly-declared copy assignment operator
If no user-defined copy assignment operators are provided for a class type (struct, class, or union), the compiler will always declare one as an inline public member of the class.
Your B class never declares a copy assignment operator (i.e. an operator with a signature compatible with B& operator=(const B&)). The fact that A does is irrelevant here. B does not declare one, so C++ generates one. That generated copy assignment operator calls the parent class' assignment operator and then assigns any variables declared in B directly, in your case nb.
I would like to be able to copy either a derived or a base object to a derived object, and I would like the correct operator to be chosen polymorphically depending on the type of the copied object.
This code does not work, I would like b1 = (A&)b2; to use B & operator= (B const &other) because b2 is a B, but it uses B & operator= (A const &other):
#include<iostream>
using namespace std;
class A {
public:
A & operator= (A const &other) {
// Here copy A members...
cout<<"A to A"<<endl;
return *this;
}
};
class B: public A {
public:
B & operator= (A const &other) {
A::operator=(other); // Copy A members.
cout<<"A to B"<<endl;
return *this;
}
B & operator= (B const &other) {
A::operator=(other); // Copy A members.
// Here copy B members...
cout<<"B to B"<<endl;
return *this;
}
};
int main()
{
B b1, b2;
A a2;
b1 = b2;
cout<<endl;
b1 = (A&)b2;
cout<<endl;
b1 = a2;
cout<<endl;
return 0;
}
I guess I have to make something virtual but I don't find how.
I would like to be able to copy either a derived or a base object to a derived object, That is a symptom of poor design.
It's better to strive for a design where the only the leaf-level classes in a class hierarchy are instantiable. This allows you to have clean, non-virtual, assignment operator functions deal only with objects of right type.
class A {
public:
// Make A uninstantiable.
virtual ~A() = 0;
A & operator= (A const &other) {
// Here copy A members...
cout<<"A to A"<<endl;
return *this;
}
};
class B: public A {
public:
// Not necessary.
// B & operator= (A const &other) { ... }
B & operator= (B const &other) {
A::operator=(other); // Copy A members.
// Here copy B members...
cout<<"B to B"<<endl;
return *this;
}
};
I have a surprising issue in VS2010.
This is the header.h of my main.cpp
class A;
class B;
class A
{
public:
double x,y;
A();
~A();
A(const A &obj);
A(const B &obj);
A& operator=(const A &obj);
};
class B
{
public:
double x,y;
B();
~B();
B(const B &obj);
B& operator=(const B &obj);
};
The main.cpp is containing the declaration of methods and :
#include "header.h"
#include <iostream>
int main() {
A t;
B u;
A a(u);
t=u;
return 0;
}
As you can see, to do
A a(u);
I had to add this method
A(const B &obj);
But for
t=u;
It uses
A& operator=(const A &obj);
Why I don't get an error ?
If you want to avoid this, you should mark your "A from B" constructor as explicit:
explicit A(const B &obj);
Any constructor not marked as explicit will be used by the compiler for implicit conversions.
by adding A(const B &obj); you provided a way to convert B to A, which is used in t=u; so the compiler can do what you ask and no error is needed. The conversion can be done because the parameter const A &obj allows conversion.
If, for whatever reason, the A(const B &obj) is meaninful but you also want t=u to fail then you have to disable implicit type conversion using the explicit keyword.
This means that only when you specifically cast B as an instance of A will the conversion be performed.
As is talked about in Item 33 in "More Effective C++", the assignment problem is
//Animal is a concrete class
Lizard:public Animal{};
Chicken:public Animal{};
Animal* pa=new Lizard('a');
Animal* pb=new Lizard('b');
*pa=*pb;//partial assignment
However, if I define Animal as an abstract base class, we can also compile and run the sentence:*pa=*pb. Partial assignment problem is still there.
See my example:
#include <iostream>
class Ab{ private: int a;
double b;
public:
virtual ~Ab()=0;
};
Ab::~Ab(){}
class C:public Ab{
private:
int a;
double b;
};
class D:public Ab{
private:
int a;
double b;
};
int main()
{
Ab *pc=new C();
Ab *pd=new D();
*pc=*pd;
return 0;
}
Do I miss something? Then what's the real meaning of the abstract base class?
I got the answer by myself. I missed a code snippet in the book.
Use protected operator= in the base class to avoid *pa=*pb. Use abstract base class to avoid animal1=animal2.Then the only allowed expressions are lizard1=lizard2;chicken1=chicken2;
See the code below:
#include <iostream>
class Ab{
private:
int a;
double b;
public:
virtual ~Ab()=0;
protected: //!!!!This is the point
Ab& operator=(const Ab&){...}
};
Ab::~Ab(){}
class C:public Ab{
public:
C& operator=(const C&){...}
private:
int a;
double b;
};
class D:public Ab{
public:
D& operator=(const D&){...}
private:
int a;
double b;
};
int main()
{
Ab *pc=new C();
Ab *pd=new D();
*pc=*pd;
return 0;
}
The abstract base class cannot help in case of assignment because the base sub-object is not instantiated (what an abstract class would block) but is sliced off the derived object (i.e. the assignment is done between already existing base sub-objects).
To avoid the problem the only solution I can think to is
make the assignment virtual
check in the assignment that the source instance is of the correct type
In code
#include <iostream>
struct Base {
int bx;
Base(int bx) : bx(bx) {}
virtual Base& operator=(const Base& other) {
bx = other.bx;
return *this;
}
};
struct A : Base {
int x;
A(int bx, int x) : Base(bx), x(x) {}
A& operator=(const Base& other) {
const A& other_a = dynamic_cast<const A&>(other);
Base::operator=(other);
x = other_a.x;
return *this;
}
};
struct B : Base {
int x;
B(int bx, int x) : Base(bx), x(x) {}
B& operator=(const Base& other) {
const B& other_b = dynamic_cast<const B&>(other);
Base::operator=(other);
x = other_b.x;
return *this;
}
};
The dynamic_cast<const A&>(other) is the operation that will fail if the object passed to the assignment operator is not of the correct derived type (it can be a sub-derived object, but this should be logically ok for an assignment source).
As an example:
int main(int argc, const char *argv[]) {
Base *pa1 = new A(1, 2);
Base *pa2 = new A(3, 4);
Base *pb1 = new B(5, 6);
Base *pb2 = new B(7, 8);
*pa1 = *pa2; std::cout << pa1->bx << "/" << dynamic_cast<A*>(pa1)->x << "\n";
*pb1 = *pb2; std::cout << pb1->bx << "/" << dynamic_cast<B*>(pb1)->x << "\n";
std::cout << "Ok so far\n";
*pa1 = *pb1; // Runtime error here (bad cast)
return 0;
}
It doesn't matter that your base class has pure virtual functions because you haven't defined the operator= for any of the classes. So when the compiler sees this statement:
*pc=*pd;
where pc and pd are both of type Ab, it will call the default assignment operator for Ab, which will result in partial assignment. As in the following example, I get the output as "Abstract Base" which is from abstract base class:
class A {
public:
virtual void foo() =0;
virtual A& operator=(const A& rhs) {
std::cout << "Abstract Base";
return *this;
}
};
class B : public A {
public:
virtual void foo() {
std::cout << "b:foo";
}
};
class C : public A {
public:
virtual void foo() {
std::cout << "c:foo";
}
};
int main()
{
A* b = new B();
A* c = new C();
*b = *c;
return 0;
}
Since you have not handled the assignment operators in your classes, you land up in situation partial assignment as Scot clearly describes in his article.
You need to handle assignments in your classes. In current design default implicit assignment operator of Ab is called and thus all the properties of children class are lost.
To avoid this you should have an implementation like this:
class Ab{ private: int a;
double b;
public:
virtual ~Ab()=0;
virtual Ab& operator=(const Ab& rhs){cout<<"in Ab="<<endl;}
};
Ab::~Ab(){}
class C:public Ab{
C& operator=(const Ab& rhs){cout<<"in C="<<endl;
return operator=(dynamic_cast<const C&>(rhs)); }
C& operator=(const C& rhs){
cout<<"do somethin in C="<<endl;
}
private:
int a;
double b;
};
class D:public Ab{
D& operator=(const Ab& rhs){cout<<"in D="<<endl;
return operator=(dynamic_cast<const D&>(rhs)); }
D& operator=(const D& rhs){
cout<<"do somethin in D="<<endl;
}
private:
int a;
double b;
};
Assignment Operator in C++ can be made virtual. Why is it required? Can we make other operators virtual too?
The assignment operator is not required to be made virtual.
The discussion below is about operator=, but it also applies to any operator overloading that takes in the type in question, and any function that takes in the type in question.
The below discussion shows that the virtual keyword does not know about a parameter's inheritance in regards to finding a matching function signature. In the final example it shows how to properly handle assignment when dealing with inherited types.
Virtual functions don't know about parameter's inheritance:
A function's signature needs to be the same for virtual to come into play. So even though in the following example, operator= is made virtual, the call will never act as a virtual function in D, because the parameters and return value of operator= are different.
The function B::operator=(const B& right) and D::operator=(const D& right) are 100% completely different and seen as 2 distinct functions.
class B
{
public:
virtual B& operator=(const B& right)
{
x = right.x;
return *this;
}
int x;
};
class D : public B
{
public:
virtual D& operator=(const D& right)
{
x = right.x;
y = right.y;
return *this;
}
int y;
};
Default values and having 2 overloaded operators:
You can though define a virtual function to allow you to set default values for D when it is assigned to variable of type B. This is even if your B variable is really a D stored into a reference of a B. You will not get the D::operator=(const D& right) function.
In the below case, an assignment from 2 D objects stored inside 2 B references... the D::operator=(const B& right) override is used.
//Use same B as above
class D : public B
{
public:
virtual D& operator=(const D& right)
{
x = right.x;
y = right.y;
return *this;
}
virtual B& operator=(const B& right)
{
x = right.x;
y = 13;//Default value
return *this;
}
int y;
};
int main(int argc, char **argv)
{
D d1;
B &b1 = d1;
d1.x = 99;
d1.y = 100;
printf("d1.x d1.y %i %i\n", d1.x, d1.y);
D d2;
B &b2 = d2;
b2 = b1;
printf("d2.x d2.y %i %i\n", d2.x, d2.y);
return 0;
}
Prints:
d1.x d1.y 99 100
d2.x d2.y 99 13
Which shows that D::operator=(const D& right) is never used.
Without the virtual keyword on B::operator=(const B& right) you would have the same results as above but the value of y would not be initialized. I.e. it would use the B::operator=(const B& right)
One last step to tie it all together, RTTI:
You can use RTTI to properly handle virtual functions that take in your type. Here is the last piece of the puzzle to figure out how to properly handle assignment when dealing with possibly inherited types.
virtual B& operator=(const B& right)
{
const D *pD = dynamic_cast<const D*>(&right);
if(pD)
{
x = pD->x;
y = pD->y;
}
else
{
x = right.x;
y = 13;//default value
}
return *this;
}
It depends on the operator.
The point of making an assignment operator virtual is to allow you from the benefit of being able to override it to copy more fields.
So if you have an Base& and you actually have a Derived& as a dynamic type, and the Derived has more fields, the correct things are copied.
However, there is then a risk that your LHS is a Derived, and the RHS is a Base, so when the virtual operator runs in Derived your parameter is not a Derived and you have no way of getting fields out of it.
Here is a good discussio:
http://icu-project.org/docs/papers/cpp_report/the_assignment_operator_revisited.html
Brian R. Bondy wrote:
One last step to tie it all together, RTTI:
You can use RTTI to properly handle virtual functions that take in your type. Here is the last piece of the puzzle to figure out how to properly handle assignment when dealing with possibly inherited types.
virtual B& operator=(const B& right)
{
const D *pD = dynamic_cast<const D*>(&right);
if(pD)
{
x = pD->x;
y = pD->y;
}
else
{
x = right.x;
y = 13;//default value
}
return *this;
}
I would like to add to this solution a few remarks. Having the assignment operator declared the same as above has three issues.
The compiler generates an assignment operator that takes a const D& argument which is not virtual and does not do what you may think it does.
Second issue is the return type, you are returning a base reference to a derived instance. Probably not much of an issue as the code works anyway. Still it is better to return references accordingly.
Third issue, derived type assignment operator does not call base class assignment operator (what if there are private fields that you would like to copy?), declaring the assignment operator as virtual will not make the compiler generate one for you. This is rather a side effect of not having at least two overloads of the assignment operator to get the wanted result.
Considering the base class (same as the one from the post I quoted):
class B
{
public:
virtual B& operator=(const B& right)
{
x = right.x;
return *this;
}
int x;
};
The following code completes the RTTI solution that I quoted:
class D : public B{
public:
// The virtual keyword is optional here because this
// method has already been declared virtual in B class
/* virtual */ const D& operator =(const B& b){
// Copy fields for base class
B::operator =(b);
try{
const D& d = dynamic_cast<const D&>(b);
// Copy D fields
y = d.y;
}
catch (std::bad_cast){
// Set default values or do nothing
}
return *this;
}
// Overload the assignment operator
// It is required to have the virtual keyword because
// you are defining a new method. Even if other methods
// with the same name are declared virtual it doesn't
// make this one virtual.
virtual const D& operator =(const D& d){
// Copy fields from B
B::operator =(d);
// Copy D fields
y = d.y;
return *this;
}
int y;
};
This may seem a complete solution, it's not. This is not a complete solution because when you derive from D you will need 1 operator = that takes const B&, 1 operator = that takes const D& and one operator that takes const D2&. The conclusion is obvious, the number of operator =() overloads is equivalent with the number of super classes + 1.
Considering that D2 inherits D, let's take a look at how the two inherited operator =() methods look like.
class D2 : public D{
/* virtual */ const D2& operator =(const B& b){
D::operator =(b); // Maybe it's a D instance referenced by a B reference.
try{
const D2& d2 = dynamic_cast<const D2&>(b);
// Copy D2 stuff
}
catch (std::bad_cast){
// Set defaults or do nothing
}
return *this;
}
/* virtual */ const D2& operator =(const D& d){
D::operator =(d);
try{
const D2& d2 = dynamic_cast<const D2&>(d);
// Copy D2 stuff
}
catch (std::bad_cast){
// Set defaults or do nothing
}
return *this;
}
};
It is obvious that the operator =(const D2&) just copies fields, imagine as if it was there. We can notice a pattern in the inherited operator =() overloads. Sadly we cannot define virtual template methods that will take care of this pattern, we need to copy and paste multiple times the same code in order to get a full polymorphic assignment operator, the only solution I see. Also applies to other binary operators.
Edit
As mentioned in the comments, the least that can be done to make life easier is to define the top-most superclass assignment operator =(), and call it from all other superclass operator =() methods. Also when copying fields a _copy method can be defined.
class B{
public:
// _copy() not required for base class
virtual const B& operator =(const B& b){
x = b.x;
return *this;
}
int x;
};
// Copy method usage
class D1 : public B{
private:
void _copy(const D1& d1){
y = d1.y;
}
public:
/* virtual */ const D1& operator =(const B& b){
B::operator =(b);
try{
_copy(dynamic_cast<const D1&>(b));
}
catch (std::bad_cast){
// Set defaults or do nothing.
}
return *this;
}
virtual const D1& operator =(const D1& d1){
B::operator =(d1);
_copy(d1);
return *this;
}
int y;
};
class D2 : public D1{
private:
void _copy(const D2& d2){
z = d2.z;
}
public:
// Top-most superclass operator = definition
/* virtual */ const D2& operator =(const B& b){
D1::operator =(b);
try{
_copy(dynamic_cast<const D2&>(b));
}
catch (std::bad_cast){
// Set defaults or do nothing
}
return *this;
}
// Same body for other superclass arguments
/* virtual */ const D2& operator =(const D1& d1){
// Conversion to superclass reference
// should not throw exception.
// Call base operator() overload.
return D2::operator =(dynamic_cast<const B&>(d1));
}
// The current class operator =()
virtual const D2& operator =(const D2& d2){
D1::operator =(d2);
_copy(d2);
return *this;
}
int z;
};
There is no need for a set defaults method because it would receive only one call (in the base operator =() overload). Changes when copying fields are done in one place and all operator =() overloads are affected and carry their intended purpose.
Thanks sehe for the suggestion.
virtual assignment is used in below scenarios:
//code snippet
Class Base;
Class Child :public Base;
Child obj1 , obj2;
Base *ptr1 , *ptr2;
ptr1= &obj1;
ptr2= &obj2 ;
//Virtual Function prototypes:
Base& operator=(const Base& obj);
Child& operator=(const Child& obj);
case 1: obj1 = obj2;
In this virtual concept doesn't play any role as we call operator= on Child class.
case 2&3: *ptr1 = obj2;
*ptr1 = *ptr2;
Here assignment won't be as expected. Reason being operator= is called on Base class instead.
It can be rectified using either:
1) Casting
dynamic_cast<Child&>(*ptr1) = obj2; // *(dynamic_cast<Child*>(ptr1))=obj2;`
dynamic_cast<Child&>(*ptr1) = dynamic_cast<Child&>(*ptr2)`
2) Virtual concept
Now by simply using virtual Base& operator=(const Base& obj) won't help as signatures are different in Child and Base for operator=.
We need to add Base& operator=(const Base& obj) in Child class along with its usual Child& operator=(const Child& obj) definition. Its important to include later definition, as in the absence of that default assignment operator will be called.(obj1=obj2 might not give desired result)
Base& operator=(const Base& obj)
{
return operator=(dynamic_cast<Child&>(const_cast<Base&>(obj)));
}
case 4: obj1 = *ptr2;
In this case compiler looks for operator=(Base& obj) definition in Child as operator= is called on Child. But since its not present and Base type can't be promoted to child implicitly, it will through error.(casting is required like obj1=dynamic_cast<Child&>(*ptr1);)
If we implement according to case2&3, this scenario will be taken care of.
As it can be seen virtual assignment makes call more elegant in case of assignments using Base class pointers/reference .
Can we make other operators virtual too? Yes
It's required only when you want to guarantee that classes derived from your class get all of their members copied correctly. If you aren't doing anything with polymorphism, then you don't really need to worry about this.
I don't know of anything that would prevent you from virtualizing any operator that you want--they're nothing but special case method calls.
This page provides an excellent and detailed description of how all this works.