class Base{
public:
virtual Base& operator=( const Base& ) {
std::cout << "Base::operator=( const Base& )" << std::endl;
return *this;
}
};
class Derived: public Base{
public:
virtual Derived& operator=( const Base& ) {
std::cout << "Derived::operator=( const Base& )" << std::endl;
return *this;
}
virtual Derived& operator=( const Derived& ) {
std::cout << "Derived::operator=( const Derived& )" << std::endl;
return *this;
}
};
int main(){
Derived obj1, obj2;
Base& ba=obj2;
obj1=ba;//outputs[1]: Derived::operator=( const Base&)
Derived& de=obj2;
obj1=de;//outputs[2]: Derived::operator=( const Derived& )
Base& bb=obj2;
bb=obj1; //outputs[3]: Derived::operator=( const Base&)
}
I am confused on the case 3 output, shouldn't assignment operator take the right operand as the function argument, so that obj1 is taken as the argument and since obj1 is class type of Derived, so it will call Derived::operator=( const Derived& )?
For the output[3], why the output is Derived::operator=( const Base& ), not Derived::operator=( const Derived& )?
The Derived::operator=( const Derived& ) has no override candidate in the Base class. I suggest adding override keyword after each method which is intended to override base class behavior. It costs nothing and can make your code much safer.
If you want to the Derived::operator=( const Derived& ) to be called, you need to provide a correct method signature in the base class:
class Derived; // forward declaration
class Base{
public:
virtual Base& operator=( const Base& ) {
std::cout << "Base::operator=( const Base& )" << std::endl;
return *this;
}
virtual Base& operator=( const Derived& ) {
std::cout << "Base::operator=( const Derived& )" << std::endl;
return *this;
}
};
class Derived: public Base{
public:
virtual Derived& operator=( const Base& ) override {
std::cout << "Derived::operator=( const Base& )" << std::endl;
return *this;
}
virtual Derived& operator=( const Derived& ) override {
std::cout << "Derived::operator=( const Derived& )" << std::endl;
return *this;
}
};
Related
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::=
I was experimenting with making the operator= be virtual, like so:
#include <iostream>
#include <string>
class Base {
public:
std::string field1;
Base(std::string str) : field1(str) {}
virtual Base& operator=(const Base& other)
{
field1 = other.field1;
return *this;
}
};
class Derived : public Base {
public:
std::string field2;
Derived(std::string str1, std::string str2) : Base(str1), field2(str2) {}
virtual Derived& operator=(const Derived& other) override
{
Base::operator=(other);
field2 = other.field2;
return *this;
}
};
However, this gives a compiler error, because the Derived function is not actually overloading anything, the signatures are different.
Is it possible to override the operator= to write code like this?
Base* ptr = new Derived("old 1", "old 2");
Derived derived("new 1", "new 2");
*ptr = derived; // <- use the derived class operator= to assign both field1 and field2
This operator
virtual Derived& operator=(const Derived& other);
does not override the copy assignment operator declared in the base class.
You have to write
Derived& operator=(const Base& other) override;
That is the type of the parameter shall be const Base &.
Here is a demonstrative program.
#include <iostream>
struct A
{
virtual A & operator =( const A & )
{
std::cout << "A::operator =\n";
return *this;
}
};
struct B : A
{
virtual B & operator =( const A &a ) override
{
A::operator =( a );
std::cout << "B::operator =\n";
return *this;
}
};
int main()
{
B b1;
A &rb1 = b1;
B b2;
b2 = rb1;
return 0;
}
Its output is
A::operator =
B::operator =
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?
Is following pattern ok/safe ? Or are there any shortcomings ?
(I also use it for equality operators)
Derived& operator=(const Derived& rhs)
{
static_cast<Base&>(*this) = rhs;
// ... copy member variables of Derived
return *this;
}
This is fine, but it's a lot more readable IMHO to call the base-class by name:
Base::operator = (rhs);
Yes, it's safe.
A different syntax to do the same thing could be:
Base::operator=( rhs );
That's better to use
Base::operator=(rhs);
because if your base class have a pure virtual method the static_cast is not allowed.
class Base {
// Attribute
public:
virtual void f() = 0;
protected:
Base& operator(const Base&);
}
class Derived {
public:
virtual void f() {};
Derived& operator=(const Derived& src) {
Base::operator=(src); // work
static_cast<Base&>(*this) = src; // didn't work
}
}