How to call derived assignment operator from base class? - c++

Given a pointer to an abstract base class A*, I want to copy or assign it (as the base class) and have the derived copy constructor or assignment operator called. I understand copy constructors cannot be virtual, so presumably the copy constructor isn't an option for doing this, but the assignment operator is. Still, it doesn't seem to work: the following code prints
assigned a
x!
destroyed b
destroyed b
which fails to assign b.
#include <iostream>
using namespace std;
class A
{
public:
virtual void x()=0;
virtual ~A() {}
virtual A& operator=(const A& other) { cout << "assigned a" << endl; return *this;}
};
class B : public A
{
public:
virtual B& operator=(const B& other) { cout << "assigned b" << endl; return *this;}
virtual void x() { cout << "x!" << endl; }
virtual ~B() { cout << "destroyed b" << endl; }
};
int main()
{
A* a = new B();
A* aa = new B();
*aa=*a;
aa->x();
delete a;
delete aa;
return 0;
}
How to do this?
EDIT this question has been correctly answered below, but it was the wrong question. I shouldn't try to override the assignment operator because I don't want subclasses of A to assign to one another. For the simpler answer (hopefully) see C++ elegantly clone derived class by calling base class

The problem is that your B::operator= does not override the one in A. Change it to
virtual A& operator=(const A& other) { cout << "assigned b" << endl; return *this;}
and it will work. Also, try to use the override keyword when overriding member functions (requires C++11). The code won't compile if you don't override. In your case, it would have caught your mistake
error: 'virtual B& B::operator=(const B&)' marked 'override', but does not override
PS: you were probably thinking about covariant return types. In order for it to work, the signature of your function has to be the same, except the return type. E.g., this will work:
virtual B& operator=(const A& other) { cout << "assigned b" << endl; return *this;}

Related

c++ assignment operator= overloading with derived classes

I'm currently writing a complicated class and in it I basically need to copy a list of derived classes. The simplified version is, as follows:
I have a base class from which I derive several other classes:
class Base
{
public:
virtual void test(void)
{
cout << "Base" << endl;
}
Base(vector<Base*> *pointer)
{
pointer->push_back(this);
}
virtual Base& operator=(const Base& rhs)
{
cout << "Base=" << endl;
return *this;
}
};
class A : public Base
{
public:
void test(void)
{
cout << "A" << endl;
}
A(vector<Base*> *pointer) : Base(pointer) {}
A& operator=(const A& rhs)
{
cout << "A=" << endl;
return *this;
}
};
class B : public Base
{
public:
void test(void)
{
cout << "B" << endl;
}
B(vector<Base*> *pointer) : Base(pointer) {}
B& operator=(const B& rhs)
{
cout << "B=" << endl;
return *this;
}
};
Then I create a list of objects, which I save in the in a pointer list of the Base class:
vector<Base*> listA;
new Base(&listA);
new A(&listA);
new B(&listA);
These objects I then want to copy in a second list with the same classes (same order), but which might have different values.
for (int i = 0; i < (int)listA.size(); i++)
{
(*listA[i]) = (*listB[i]);
}
However c++ is not able to do that. Because the list has the type Base*, dereferencing creates an object of type Base. Therefore the assignment operator= of the Base class is called instead of the correct one from the derived class. How can I fix this?
Or how can I tell c++ to use the right operator? Maybe by some isinstanceof-function?
For a full sample see:
int main()
{
vector<Base*> listA;
new Base(&listA);
new A(&listA);
new B(&listA);
vector<Base*> listB;
new Base(&listB);
new A(&listB);
new B(&listB);
for (int i = 0; i < (int)listA.size(); i++)
{
(*listA[i]).test();
}
for (int i = 0; i < (int)listA.size(); i++)
{
(*listA[i]) = (*listB[i]);
}
}
Which outputs:
Base
A
B
Base=
Base=
Base=
There are a few misunderstandings here. First and foremost, what does it mean to assign an instance of a derived class to an instance of a base class? Let's take a simple hierarchy:
struct A { int x; };
struct B : A { int y; };
A a;
B b;
a = b; // what should this do?
b = a; // what about this?
With normal C++, the first one does object slicing, and the second one is ill-formed. But even the first one, well well-formed, is typically not what you want to do anyway. Are you sure you want to be slicing?
The second is that while you made your assignment operator virtual:
virtual Base& operator=(const Base& rhs)
None of the derived classes actually override it. A's assignment operator takes an A const& and B's takes a B const&. If you marked the two with override, your compiler would point this out to you. If you fix those two to take a Base const& argument, then you would get what you want printed - but it's probably still not what you actually want to have happen.
In order to actually make polymorphic copies, a typical solution is to provide a virtual clone method:
virtual Base* clone() const = 0;
That your derived classes implement:
struct A : Base {
A* clone() const override { return new A(*this); }
};
And then use clone() instead of assignment. There will be no slicing here.
Insert the usual caveats about memory management and raw pointers here.
Okay. I found a solution for my problem. I implemented a copy function which takes the Base class as the argument. Inside this copy function I can copy the variables using the pointa. The classe now are as follows:
class Base
{
public:
virtual void test(void)
{
cout << "Base" << endl;
}
Base(vector<Base*> *pointer)
{
pointer->push_back(this);
}
virtual void clone(Base* pointer) = 0;
};
class A : public Base
{
public:
void test(void)
{
cout << "A" << endl;
}
A(vector<Base*> *pointer) : Base(pointer) {}
void clone(Base* pointer) override
{
A* pointa = (A*)pointer;
cout << "clone A" << endl;
//Clone Variables here
}
};
class B : public Base
{
public:
void test(void)
{
cout << "B" << endl;
}
B(vector<Base*> *pointer) : Base(pointer) {}
void clone(Base* pointer) override
{
B* pointa = (B*)pointer;
cout << "clone B" << endl;
//Clone Variables here
}
};
This means I can now copy the objects in the following way:
for (int i = 0; i < (int)listA.size(); i++)
{
listA[i]->clone(listB[i]);
}
However this solution is not in any way typesafe, a requirement I want to meet. I looked into my idea, and decided to do things manually without the list, which means lot's of duplicated code, but brings peace of mind.

Default constructor is getting called on a const reference member despite non default constructor arguments

Here is some basic C++ outline of code:
#include <cstdlib>
#include <iostream>
#include <thread>
using namespace std;
class M {
public:
M() = default;
~M() {
cout << "Called ~M" << endl;
}
};
class A {
public:
A(int z) : _z(z) {
cout << "Called A(int z)" << endl;
this_thread::sleep_for(chrono::milliseconds(1000));
}
A() {
cout << "Called A()" << endl;
this_thread::sleep_for(chrono::milliseconds(1000));
}
A(const A& a) {
cout << "Called A(const A& a)" << endl;
_z = a._z;
}
A(const A&& a) {
cout << "Called A(const A&& a)" << endl;
_z = a._z;
}
A& operator=(const A& a) {
cout << "Called A& operator=(const A& a)" << endl;
if (&a != this) {
cout << "Executed A& operator=(const A& a)" << endl;
}
}
virtual ~A() {
cout << "Called ~A" << endl;
}
int poll() const { return _z; }
private:
M _m;
int _z = 300;
};
class B : public A {
public:
// _a(10)
B() : _a(std::move(A(10))) {
cout << "Called B()" << endl;
}
virtual ~B() {
cout << "Called ~B" << endl;
}
private:
const A& _a;
};
int main(int argc, char** argv) {
B b;
A* aPtr = &b;
A& aRef = (*aPtr);
cout << aRef.poll() << endl;
return 0;
}
from the setup above I get the following output:
Called A()
Called A(int z)
Called ~A
Called ~M
Called B()
300
Called ~B
Called ~A
Called ~M
My issue is the first line of the output (all the others make sense given the first). I am initializing the member _a in B() : _a(std::move(A(10))), this is forced as _a is const reference member. And the CTOR with int argument gets called as well, however why is the default CTOR called on A? Why no move CTOR? Therefore the temporary object simply seems constructed and destroyed, no real move is happening (as can be seen from the 300 output later on).
Now this issue does not seem related to the move per se but to the behaviour around the const reference member. Because if I change the initialization list to: B(): _a(10) I get the same issue: somehow the default object is assigned to the const reference member and the arguments in the initialization list are ignored. So for B(): _a(10) I get:
Called A()
Called A(int z)
Called B()
300
Called ~B
Called ~A
Called ~M
Basically why is the first line a default constructor? And how do I alter the code so that the 10 from the initialization appears instead of the 300 from the default?
Each object of type B has actually two subobjects of type A. One is the base-class subobject, and the other is the _a member subobject. You call the constructor for the member, but the base-class subobject is default-initialized since you haven't explicitly called its constructor in your initialization list.
You could do it by, for example, the following:
B() : A(arguments) //<--initialize the base-class subobject
, _a(std::move(A(10))) {
cout << "Called B()" << endl;
}
Your B both contains an instance of A and derives from A (which is probably a mistake).
You're passing 10 when you construct a temporary A object, then moving that into the member _a. You're leaving the base class subobject to be default initialized.
To fix that, you need to include the base class in the member initializer list:
B() : A(1010), _a(std::move(A(10))) {
cout << "Called B()" << endl;
}
This initializes the base class subobject of B with 1010 (to distinguish it from the member object).
If I were going to do this, I'd also initialize _a directly, so the ctor would look something like:
B() : A(1010), _a(10) { // ...

Why is not overloaded function for derived class object invoked when given a pointer to base class in C++?

In the following code
#include <iostream>
using namespace std;
class A {
public:
A() {}
virtual ~A() {};
};
class B : public A {
public:
B() {}
virtual ~B() {};
};
void process(const A&) {
cout << "processing A" << endl;
}
void process(const B&) {
cout << "processing B" << endl;
}
int main(void) {
A* a = new B;
process(*a);
return 0;
}
the output of running it becomes
processing A
but I would have assumed that it should have been
processing B
since a points to the derived class B and not A. So why does it call the first implementation of process function and not the second?
The static type of expression *a is A because a was declared as
A* a = new B;
The compiler resolves the selection of overloaded functions using the static type of the argument.
Even when virtual functions are called the compiler uses the static type of the object to call appropriate function. The difference is only that the compiler uses the table of pointers to virtual functions to indirectly call the required function.
You need to make process() a virtual member function of A, B:
class A {
public:
A() {}
virtual ~A() {};
virtual void process() const { cout << "processing A" << endl; }
};
class B : public A {
public:
B() {}
virtual ~B() {};
virtual void process() const override { cout << "processing B" << endl; }
};
int main(void) {
A* a = new B;
a->process();
return 0;
}
In your current code, *a is of type A&, so the closest match to process(*a); is the first overload (for const A&).
void process(const A&); is a better (exact) match, since dereferencing A* gives you A&.
Short answer, but there isn't much more to say unless you want a reference from the standard.
You could dynamic_cast the result of *a and that would give you a B&, but that's smelly desing. What you probably want is a virtual function in A that's overriden in B (assume it's called foo). Then, calling a->foo() would dispatch to B::foo.

Why doesn't a derived class use the base class operator= (assignment operator)?

Following is a simplified version of an actual problem. Rather than call Base::operator=(int), the code appears to generate a temporary Derived object and copy that instead. Why doesn't the base assignment operator get used, since the function signature seems to match perfectly? This simplified example doesn't display any ill effects, but the original code has a side-effect in the destructor that causes all kinds of havoc.
#include <iostream>
using namespace std;
class Base
{
public:
Base()
{
cout << "Base()\n";
}
Base(int)
{
cout << "Base(int)\n";
}
~Base()
{
cout << "~Base()\n";
}
Base& operator=(int)
{
cout << "Base::operator=(int)\n";
return *this;
}
};
class Derived : public Base
{
public:
Derived()
{
cout << "Derived()\n";
}
explicit Derived(int n) : Base(n)
{
cout << "Derived(int)\n";
}
~Derived()
{
cout << "~Derived()\n";
}
};
class Holder
{
public:
Holder(int n)
{
member = n;
}
Derived member;
};
int main(int argc, char* argv[])
{
cout << "Start\n";
Holder obj(1);
cout << "Finish\n";
return 0;
}
The output is:
Start
Base()
Derived()
Base(int)
Derived(int)
~Derived()
~Base()
Finish
~Derived()
~Base()
http://ideone.com/TAR2S
This is a subtle interaction between a compiler-generated operator= method and member function hiding. Since the Derived class did not declare any operator= members, one was implicitly generated by the compiler: Derived& operator=(const Derived& source). This operator= hid the operator= in the base class so it couldn't be used. The compiler was still able to complete the assignment by creating a temporary object using the Derived(int) constructor and copy it with the implicitly generated assignment operator.
Because the function doing the hiding was generated implicitly and wasn't part of the source, it was very hard to spot.
This could have been discovered by using the explicit keyword on the int constructor - the compiler would have issued an error instead of generating the temporary object automatically. In the original code the implicit conversion is a well-used feature, so explicit wasn't used.
The solution is fairly simple, the Derived class can explicitly pull in the definition from the Base class:
using Base::operator=;
http://ideone.com/6nWmx

Can someone explain this behaviour for virtual function?

I know about the basic concept of virtual function and run-time call. But i tried
running some piece of code which confused me
class A {
public:
A& operator=(char) {
cout << "A& A::operator=(char)" << endl;
return *this;
}
virtual A& operator=(const A&) {
cout << "A& A::operator=(const A&)" << endl;
return *this;
}
};
class B : public A {
public:
B& operator=(char) {
cout << "B& B::operator=(char)" << endl;
return *this;
}
virtual B& operator=(const B&) {
cout << "B& B::operator=(const B&)" << endl;
return *this;
}
};
int main() {
B b1;
B b2;
A* ap1 = &b1;
A* ap2 = &b1;
*ap1 = 'z';
*ap2 = b2;
}
Running this program give me the following output:-
A& A::operator=(char) //expected output
A& A::operator=(const A&) //Why this Output? in case of *ap2 = b2;
b2 is an object of B type but still it goes in virtual A& operator=(const A&)
and not virtual B& operator=(const B&). Why is this so ?
Because virtual B& operator=(const B&) does not override virtual A& operator=(const A&); the arguments are different.
For a derived class function to override Base class function, the derived class function needs to have the exact same function prototype(exception: covariant return types are allowed).
The = operator in derived class B here does not have same function prototype as = in Base class A, and hence it does not override the Base class =.
The only = operator available is the one which is called.
For a function to be considered an override the signature has to match the version in tha base class exactly (well, the return type may be covariant if a pointer or a reference is returned). That is, you would need to define
B& B::operator= (A const&)
to override the version from the base class. Note that for input parameters in overriding functions it wouldn't make sense to be covariant because you can't guarantee that the base class version is called with a derived object in a context using only the base class. If anything parameters to an overriding function could be contravariant but C++ doesn't support this.
Here in the derived class the function is taking B whereas in base class its taking A. So, basically its not being overridden as the function arguments are different.
Also note that the return type in case of overriding may be different as in your case you are returning reference of A in base and reference of B in derived.
virtual Base& func(const Base&)
virtual Derived& func(const Base&)
This is valid form of overriding