I was wondering how should I (or can I? does it make any sense?) overload the assignment operator when working with inheritance and upcasting?
Let's say we have Base class and Derived class (inherited from Base). If i have something like:
/// supose we have one overloaded assignment operator in Base like Base& operator=(const Base&) and
///one in Derived like Derived& operator=(const Derived&)...
Base* a, *b;
Derived c,d;
a = &c;
b = &d;
*a = *b /// this will call the function in Base
If that calls the Base function, why should I overload "=" again in Derived? Is overloading assignment operator in Derived necessary only for working directly with objects, not upcasting (pointers) ?
Here's some code that hopefully helps you out.
Derived Does Not Own A Dynamic Resource
The Base class holds a dynamic resource, so we are required to follow the Rule of 3 (should be 5, but kept it at 3 for brevity). I did so by utilizing the copy/swap idiom.
I then derive Derived from Base. It does not hold a dynamic resource, so I follow the Rule of 0 and don't provide a custom copy constructor, destructor, assignment operator, move constructor, or move assignment.
You can see from the output that the Base portion of the Derived objects are able to deep-copy themsleves just fine, and the Derived-only portion gets by just fine with shallow copy. The final output leaks memory, but I chose to do that to demonstrate an actual overwrite using a pointer to Base.
#include <iostream>
class Base {
private:
int* m = nullptr;
public:
Base() = default;
Base(int v) : m(new int(v)) {}
Base(const Base& other) : m(new int(*(other.m))) {}
virtual ~Base() {
delete m;
m = nullptr;
}
Base& operator=(Base other) {
swap(*this, other);
return *this;
}
friend void swap(Base& lhs, Base& rhs) {
using std::swap;
swap(lhs.m, rhs.m);
}
virtual void print() const {
std::cout << "Address: " << m << "\nValue: " << *m << '\n';
}
};
class Derived : public Base {
private:
double x = 0.0;
public:
Derived() = default;
Derived(double v) : Base(), x(v) {}
Derived(int i, double v) : Base(i), x(v) {}
void print() const override {
std::cout << "Address: " << &x << "\nValue: " << x << '\n';
Base::print();
}
};
int main() {
std::cout << "A\n";
Base* a = new Derived(5, 3.14);
a->print();
std::cout << "\nB\n";
Derived b = *(dynamic_cast<Derived*>(a)); // Copy ctor
b.print();
std::cout << "\nC\n";
Derived c;
c = b;
c.print();
std::cout << "\nReplace A (This leaks)\n";
a = new Derived(7, 9.81);
a->print();
}
Output:
A
Address: 0x21712d0
Value: 3.14
Address: 0x21712e0
Value: 5
B
Address: 0x7ffdd62964c8
Value: 3.14
Address: 0x2171300
Value: 5
C
Address: 0x7ffdd62964b0
Value: 3.14
Address: 0x2171320
Value: 5
Replace A (This leaks)
Address: 0x2171350
Value: 9.81
Address: 0x2171360
Value: 7
Derived Owns A Dynamic Resource
Now, Derived has a dynamic of its own to manage. So I follow the Rule of 3 and provide a copy constructor, destructor, and assignment operator overload. You'll notice that the assignment operator looks identical to the Base version; this is intentional.
It's because I'm using the copy/swap idiom. So in the swap() function for Derived, I add a step where it swaps the Base portion, then swaps the Derived portion. I do this by invoking the Base swap() function through the dynamic cast.
And we can again observe that all objects have their own memory for each dynamically allocated piece.
#include <iostream>
class Base {
private:
int* m = nullptr;
public:
Base() = default;
Base(int v) : m(new int(v)) {}
Base(const Base& other) : m(new int(*(other.m))) {}
virtual ~Base() {
delete m;
m = nullptr;
}
Base& operator=(Base other) {
swap(*this, other);
return *this;
}
friend void swap(Base& lhs, Base& rhs) {
using std::swap;
swap(lhs.m, rhs.m);
}
virtual void print() const {
std::cout << "Address: " << m << "\nValue: " << *m << '\n';
}
};
class Derived : public Base {
private:
double* x = nullptr;
public:
Derived() = default;
Derived(double v) : Base(), x(new double(v)) {}
Derived(int i, double v) : Base(i), x(new double(v)) {}
Derived(const Derived& other) : Base(other), x(new double(*(other.x))) {}
~Derived() {
delete x;
x = nullptr;
}
Derived& operator=(Derived other) {
swap(*this, other);
return *this;
}
friend void swap(Derived& lhs, Derived& rhs) {
using std::swap;
swap(dynamic_cast<Base&>(lhs), dynamic_cast<Base&>(rhs));
swap(lhs.x, rhs.x);
}
void print() const override {
std::cout << "Address: " << &x << "\nValue: " << *x << '\n';
Base::print();
}
};
int main() {
std::cout << "A\n";
Base* a = new Derived(5, 3.14);
a->print();
std::cout << "\nB\n";
Derived b = *(dynamic_cast<Derived*>(a)); // Copy ctor
b.print();
std::cout << "\nC\n";
Derived c;
c = b;
c.print();
std::cout << "\nReplace A (This leaks)\n";
a = new Derived(7, 9.81);
a->print();
}
Output:
A
Address: 0x14812d0
Value: 3.14
Address: 0x14812e0
Value: 5
B
Address: 0x7fffe89e8d68
Value: 3.14
Address: 0x1481320
Value: 5
C
Address: 0x7fffe89e8d50
Value: 3.14
Address: 0x1481360
Value: 5
Replace A (This leaks)
Address: 0x14813b0
Value: 9.81
Address: 0x14813c0
Value: 7
Does the output of this example help clarify your question? You could always override the operator= in the derived class as follows:
#include <cstdio>
struct Base{
virtual ~Base() = default;
virtual void operator=(const Base&) {
std::printf("Base::=\n");
}
};
struct Derived: public Base {
void operator=(const Derived&) {
std::printf("Derived::=\n");
}
void operator=(const Base&) override{
std::printf("Derived::= Base\n");
}
};
int main() {
Base* a, *b;
Derived c,d;
a = &c;
b = &d;
*a = *b; //Dispatches the call to the derived class =
Base base;
Derived derived;
derived = base; //Usual case now after operator=(const Base&) in Derived
c = d; //Usual case
Base base1, base2;
base1 = base2; //Usual case
a = &base1;
b = &base2;
*a = *b; //Usual case
}
Output:
Derived::= Base
Derived::= Base
Derived::=
Base::=
Base::=
Related
I am working on a system where a class has a private member that is a base class pointer. The pointer can be pointing to an inherited class object. I would like the parameterized and copy constructors to be able to take a pointer of the base class type and deep copy it without erasing the inherited type. Here is some code I made to demonstrate this problem; I would like c2 and c3 to call B.print instead of A.print, but can't figure out how.
#include <iostream>
class A
{
protected:
double a;
double b;
public:
A()
{
a = 0.0;
b = 0.0;
}
A(double a, double b)
{
this->a = a;
this->b = b;
}
A(const A& copy)
{
a = copy.a;
b = copy.b;
}
virtual void print()
{
std::cout << "print A" << std::endl;
std::cout << "a: " << a << ", b: " << b << std::endl;
}
};
class B : public A
{
private:
double c;
public:
B()
{
c = 0.0;
}
B(double a, double b, double c) : A(a, b)
{
this->c = c;
}
B(const B& copy) : A(copy)
{
c = copy.c;
}
virtual void print()
{
std::cout << "print B" << std::endl;
std::cout << "a: " << a << ", b: " << b << ", c: " << c << std::endl;
}
};
class C
{
private:
A* a;
public:
C()
{
a = nullptr;
}
C(A* a)
{
this->a = new A(*a);
}
C(const C& copy)
{
a = nullptr;
if (copy.a != nullptr)
a = new A(*copy.a);
}
~C()
{
delete a;
a = nullptr;
}
void print()
{
if (a != nullptr)
a->print();
}
};
int main()
{
A* a = new A(3.5, 4.5);
A* b = new B(3.5, 4.5, 5.5);
C c1(a), c2(b), c3(c2);
delete a, b;
c1.print();
c2.print();
c3.print();
}
I have tried including an enum of the available types and storing that value separately, but that program was non-extensible as it would require changing a switch and enum for each added inherited type.
Your actual problem is with these lines:
C(A* a) {
this->a = new A(*a);
}
C(const C& copy) {
a = nullptr;
if (copy.a != nullptr)
a = new A(*copy.a);
}
which explicitly create an object with dynamic type A, ignoring the dynamic types of a and copy.a.
The usual pattern for copying something's real dynamic type is
struct Base
{
// copy ctor etc. as usual
virtual Base* clone() const { return new Base(*this); }
};
struct Derived: Base
{
virtual Base* clone() const { return new Derived(*this); }
};
Other, miscellaneous notes:
You don't need to burn 6 lines on a default constructor that does nothing any more. Just write
class A
{
protected:
double a{};
double b{};
and let the compiler default it. You can default most of your copy constructors too.
If you're controlling pointer lifetime (as C is), just use std::unique_ptr. It's there, it's free, it's much more explicit about your semantics.
When you're owning (ie, controlling the lifetime of) a polymorphic object via a base-class pointer, that base class needs to have a virtual destructor (unless you're doing something exotic with a type-erased deleter, which is out of scope here).
If you've decided to use this->a for data members, use it consistently. If you're only using it for disambiguation, just change the parameter/local variable name so it doesn't collide in the first place.
There are member-naming conventions like m_a or just a_ which make it easy to see which variables are data members, remove ambiguity, and are still less typing/visual noise than randomly sprinkling this-> around.
Working code with only the changes described above:
#include <iostream>
#include <memory>
class A
{
protected:
double a_{};
double b_{};
public:
A() = default;
A(double a, double b) : a_(a), b_(b) {}
A(const A&) = default;
virtual ~A() = default;
virtual A* clone() const { return new A(*this); }
virtual void print()
{
std::cout << "print A" << '\n'
<< "a: " << a_ << ", b: " << b_ << '\n';
}
};
class B : public A
{
double c_{};
public:
B() = default;
B(double a, double b, double c) : A(a, b), c_(c) {}
B(const B&) = default;
virtual B* clone() const override { return new B(*this); }
virtual void print() override
{
std::cout << "print B" << '\n'
<< "a: " << a_ << ", b: " << b_ << ", c: " << c_ << '\n';
}
};
// owns and deep-copies an A.
// class invariant: ptr_ is non-null
//
class C
{
private:
std::unique_ptr<A> ptr_;
public:
explicit C(A* a) : ptr_(a->clone()) {}
C(const C& copy) : ptr_(copy.ptr_->clone()) {}
void print()
{
ptr_->print();
}
};
int main()
{
A a{3.5, 4.5};
B b{3.5, 4.5, 5.5};
C c1(&a), c2(&b), c3(c2);
c1.print();
c2.print();
c3.print();
}
NB. The top-level dynamic allocation was unnecessary even if you do want C to deep copy, so I just removed it entirely instead of either changing it to also use unique_ptr or fixing that incorrect delete statement.
I have the following code:
#include <iostream>
using namespace std;
class Base {
public:
Base operator/(const Base& other){
Base res;
cout << "Base /" << endl;
return res;
}
Base& operator/=(const Base& other){
cout << "Base /=" << endl;
return *this;
}
};
class Derived : public Base {
public:
Derived operator/(const Derived& other){
Derived res;
cout << "Derived /" << endl;
return res;
}
};
int main() {
Derived d1, d2;
Base b1, b2;
b1 = d1 / d2;
b2 = d1 /= d2;
}
The second assignment outputs Base /=. Can I achieve that the second assignment to use the overridden operator/, without overriding operator/=? I think I have to implement one operator using the other.
This is a homework, so it is OK to give just the basic idea what to do.
Though being clearly not the preferred way and probably some sort of "ugly", it is possible to use Derived::operator/ without providing a Derived::operator/=. To do that, the following steps are necessary:
call operator / in the implementation of Base::operator/=
Make operator / virtual such that it is not statically bound in /=-implementation
Provide an operator/-implementation in class Derived that overrides the one in the Base, i.e. provide an operator virtual Base operator/(const Base&) in addition to Derived operator/(const Derived&) (note that the latter cannot override since it has a covariant argument type)
In this Derived::operator/(const Base& other)-implementation, check the dynamic type of other and explicitly call the respective implementation.
See the following code:
class Base {
public:
virtual Base operator/(const Base& other) const {
Base res;
cout << "Base /" << endl;
return res;
}
Base& operator/=(const Base& other){
cout << "Base /=" << endl;
*this = *this / other;
return *this;
}
};
class Derived : public Base {
public:
virtual Base operator/(const Base& other) const override {
cout << "Derived /(Base&)" << endl;
if(dynamic_cast<const Derived*>(&other)==0) {
// do something specific here, or call base operator:
return Base::operator/(other);
}
else {
return operator/(dynamic_cast<const Derived&>(other));
}
}
Derived operator/(const Derived& other) const { // cannot override
cout << "Derived /(Derived&)" << endl;
return *this;
}
};
int main() {
Derived d1, d2;
Base b1, b2;
b1 = d1 / d2;
// Output: Derived /(Derived&)
b2 = d1 /= d2;
// Output:
// Base /=
// Derived /(Base&)
// Derived /(Derived&)
return 0;
}
Be aware that an expression Derived d3 = d2 /= d1 is still not possible, because return type Base & of operator Base& Base::operator /= cannot be converted to Derived &.
#include <iostream>
using namespace std;
class Base
{
public:
Base () {cout << "\nBase Default Ctor";}
Base (const Base& aObj) {cout << "\nCopy Ctor";}
Base& operator=(const Base& aObj) {cout << "\nBase assignment operator";}
~Base() {cout << "\nBase Destructor";}
};
class Derived : public Base
{
public:
Derived () {cout << "\nDerived Default Ctor";}
Derived (const Derived& aObj) {cout << "\nDerived Copy Ctor";}
Derived (const Base& aObj) {cout << "\nDerived Copy Ctor using Base Object";}
Derived& operator=(const Derived& aObj) {cout << "\nDerived Assignment operator";}
Derived& operator=(const Base& aObj) {cout << "\nDerived Assignment operator using Base Object";}
~Derived () {cout << "\nDerived Destructor";}
};
int main ()
{
Base bObj, bObj1;
Derived dObj, dObj2;
Derived dObj1 = bObj;
dObj2 = bObj1;
return 0;
}
Here I used two techniques to create derived object from base object.
Copy constructor: Derived dObj1 = bObj;
Assignment operator: first derived object is created with default constructor then base object is assigned to derived.
Which approach is better and why?
I have a base class Base and a derived class D, and I'd like to have move constructor and move assignment operator automatically generated by the compiler for me. Following the Rule of Zero, I leave all memory management to the compiler and only use level-2 classes (no raw pointers, arrays, etc.):
#include <iostream>
class Base{
public:
Base(): a_(42) {}
virtual void show() { std::cout << "Base " << a_ << std::endl; }
private:
int a_;
};
class D : Base {
public:
D(): b_(666) {}
void show() { std::cout << "D " << b_ << std::endl; }
private:
int b_;
};
int main() {
Base b;
b.show();
D d;
d.show();
return 0;
}
This should be it, right?
Enter the C++ core guidelines:
A base class destructor should be either public and virtual, or protected and nonvirtual.
Ah, so I guess I'll have to add a destructor to Base. But that'll do away with the automatically generated move functions!
What's the clean way out here?
You can = default everything that you would like to be generated by the compiler.
See (at the bottom): http://en.cppreference.com/w/cpp/language/rule_of_three
In your case it could look something like:
class Base{
public:
Base(): a_(42) {}
Base(const Base&) = default;
Base(Base&&) = default;
Base& operator=(const Base&) = default;
Base& operator=(Base&&) = default;
virtual ~Base() = default;
virtual void show() { std::cout << "Base " << a_ << std::endl; }
private:
int a_;
};
You can create once a class like
struct VirtualBase
{
virtual ~VirtualBase() = default;
VirtualBase() = default;
VirtualBase(const VirtualBase&) = default;
VirtualBase(VirtualBase&&) = default;
VirtualBase& operator = (const VirtualBase&) = default;
VirtualBase& operator = (VirtualBase&&) = default;
};
And then follow rule of zero:
class Base : VirtualBase
{
public:
Base(): a_(42) {}
virtual void show() { std::cout << "Base " << a_ << std::endl; }
private:
int a_;
};
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;
};