avoiding duplication in the assignment operator of a derived class - c++

Consider the assignment operators in classes Parent and Child, below.
#include <iostream>
class Parent
{
public:
Parent(){};
virtual ~Parent(){};
Parent& operator=(const Parent& other){mP = other.mP; return *this;};
void setP(double inP){mP = inP;};
double getP(){return mP;};
protected:
double mP;
};
class Child : public virtual Parent
{
public:
Child(){};
virtual ~Child(){};
Child& operator=(const Child& other)
{
mC = other.mC;
mP = other.mP;// this line
return *this;
};
void setC(double inC){mC = inC;};
double getC(){return mC;};
protected:
double mC;
};
Is here a way to avoid the duplicate line mP = other.mP;?
The reason I am asking is that as the number of bases get higher and the inheritance structure gets more complicated, it is easy to lose track of members.
EDIT
The reason I need to implement the operator= is that it needs to check some things before the assignments.

Just call the Parent operator:
Child& operator=(const Child& other)
{
mC = other.mC;
Parent::operator=(other);
return *this;
}
Or really, don't implement either operator since both are trivial!

The best way to avoid this issue is to remove both of your operator= functions.
The compiler-generated operator= applies operator= to each member variable and base class, which is just what you were trying to do anyway.
Re-implementing the wheel just makes your code harder to read and maintain -- and sometimes less efficient.

Child& operator=(const Child& other) {
mC = other.mC;
mP = other.mP;
}
you can invoke the assignment operator of the parent prior to the child specific assignment in this way:
Child& operator=(const Child& other)
{
Parent::operator=(other);
mC = other.mC;
return *this;
};

Related

Implementing copy constructor for polymorphic class

Suppose I'm dealing with the following header file(for experimental purposes):
class Base{
protected:
Data& data1;
public:
Base(Data& data1);
virtual Base* clone() const =0;
virtual ~Base();
virtual void do() = 0;
};
class Derived: public Base{
private:
const int data2;
public:
Derived(int data2, Data& data1);
virtual Derived* clone() const;
~Derived();
}
I tried to implement these classes which are fairly simple:
Base:
Base::Base(Data& data1): data1{data1} {}
virtual Base::~Base() = default;
Derived:
Derived::Derived(int data2, Data& data1): Base(data1), data2{data2} {}
virtual Derived* Derived::clone() const {
return new Derived(*this);
}
virtual void Derived::do() {
return;
}
So far so good, but I'm having a couple of issues:
Say I wanted to implement a copy constructor and a copy assignment operator for this hierarchy,
should I do it in both classes? If so, is this a good way to do it? :
Base:
Base::Base(const Base& other): Base(other.data1) {}
Base& Base::operator=(const Base& rhs){
if(this == &rhs)
return *this;
data1 = rhs.data1; // is this possible? since data1 is a reference (cannot be reassigned)
return *this;
}
and which is a proper way to do so for derived?:
Derived:
Derived::Derived(const Derived& other): Base(other.data1), data2{other.data2} {}
OR
Derived::Derived(const Derived& other): Derived(other.data2, other.data2) {}
Derived& Derived::operator=(const Derived& rhs){
if(this == &rhs)
return *this;
data1 = rhs.data1;
data2 = rhs.data2; // data2 is a const variable so this is probably invalid too
return *this;
}
Once I implement a copy constructor for derived, I get an 'Endless loop' warning when returning from clone() method. Why is that?
Is there a proper way to implement rule of 3/ rule of 5 for classes of such hierarchy?
Thanks ahead.
Say I wanted to implement a copy constructor and a copy assignment operator for this hierarchy, should I do it in both classes? If so, is this a good way to do it?
Yes, you have to provide copy constructor/assignment.
Best way, when possible/correct, is to use default.
For assignment, your issue is unrelated to hierarchy, but related to member you use:
Reference cannot be rebound. const member cannot be changed.
So default assignment cannot be generated, mark them as deleted.
class Base
{
// ...
Base(const Base&) = default;
Base& operator=(const Base&) = delete;
};
class Derived
{
// ...
Derived(const Derived&) = default;
Derived& operator=(const Derived&) = delete;
};
Once I implement a copy constructor for derived, I get an 'Endless loop' warning when returning from clone() method. Why is that?
Cannot reproduce from comment
Is there a proper way to implement rule of 3/ rule of 5 for classes of such hierarchy?
Hierarchy is unrelated to rule of 5/3/0.
Just respect rule of 5/3/0 for each class.

Regarding c++ operator assign behavior when have parent child relationship

I have a question about operator= that accepts parent reference type.
When there is a abstract class and it's implemented class,
why is not enough to have single operator= that accept parent reference type?
Below is my code
#include <iostream>
class AbstractType {
public:
virtual ~AbstractType() {}
virtual AbstractType& operator=(const AbstractType& other) {
std::cout << "AbstractType& operator=(const AbstractType&)" << std::endl;
return *this;
}
};
class Type1: public AbstractType {
public:
virtual ~Type1() {}
virtual Type1& operator=(const AbstractType& other) {
std::cout << "Type1& operator=(const AbstractType&)" <<
std::endl;
return *this;
}
/*
virtual Type1& operator=(const Type1& other) {
std::cout << "Type1& operator=(const Type1&)" << std::endl;
// Just redirecting here! What a waste!
return operator=(dynamic_cast<const AbstractType&>(other));
}
*/
};
int main()
{
Type1 t1;
AbstractType& r1 = t1;
Type1 t2;
AbstractType& r2 = t2;
// Type1& operator=(const AbstractType&). It's fine!
r1 = r2;
// AbstractType& operator=(const AbstractType&)!! Why??
// Expected `Type1& operator=(const AbstractType&)` to be called!
t1 = t2;
return 0;
}
You can find given parameter is being just redirected in Type1& operator=(const Type1&) that is ignored by comment.
Uncomment Type1& operator=(const Type1&) is just working for me,
but if say, I have more than a hundred of TypeX then I have to make two hundred of copy assignment which is I can't understand because it seems to me just having Type1& operator=(const AbstractType& other) is enough.
And most cases I only have AbstractType to handle things around.
Very rare to know it's specific type in advance under limited situations.
Anyone can suggest me better solution?
// AbstractType& operator=(const AbstractType&)!! Why??
// Expected `Type1& operator=(const AbstractType&)` to be called!
t1 = t2;
Here you are calling:
t1.operator=(t2);
Since t1 and t2 have Type1, the compiler will match the following function:
Type1 & Type1::operator=(const Type1 &);
which is the implicitly-defined copy assignment operator, and which will call the copy assignment operator of the base:
AbstractType & AbstractType::operator=(const AbstractType &);
However, this call is not dynamically dispatched -- which is the reason you end up seeing your results.
Uncomment Type1& operator=(const Type1&) is just working for me
Note that:
There is no need for dynamic_cast.
There is no need to make a virtual call.
The operator itself does not need to be virtual.
In other words, you can simplify to:
Type1& operator=(const Type1& other)
{
return Type1::operator=(static_cast<const AbstractType&>(other));
}
Because of Liskovs substitution principle, which states that if a program, module or function is using a Base class, then the reference of Base class can be replaced with Derived class without affecting programs functionality. So in your particular case, implementing it in terms of Curiously recurring template pattern will be an elegant solution. Please see link for more info!

C++ copy constructors and assignments in cloneable hierarchy

There is well-known clone idiom for copying Derived objects via pointer to Base class.
class Base{
int b;
public:
virtual unique_ptr<Base> clone() const = 0;
virtual ~Base() = default;
};
class Derived : public Base {
int d;
public:
virtual unique_ptr<Base> clone() const override {
return std::make_unique<Derived>(*this);
}
}
However, I can't find clear instructions how to define copy constructors and assignments in this case. This is how I suppose it should be done in Base class:
class Base {
protected:
Base(const Base&) = default;
private:
Base& operator=(const Base&) = delete;
}
Is it necessary (in order to avoid potential slices)? Is it right way to do it? Does it suffice or should I add such declarations to Derived class as well?
As derived classes use the copy constructor to create clones, you may like to make the copy constructors non-public to avoid accidental slicing but accessible to derived classes.
protected fills this requirement. It needs to be applied to each class' copy constructor because the compiler-generated copy constructor is public. It also makes sense to apply the same treatment to the assignment operator.
That also prevents std::make_unique from accessing the copy constructor though:
class A
{
protected:
A(A const&) = default;
A& operator=(A const&) = default;
public:
A();
virtual std::unique_ptr<A> clone() const = 0;
};
class B : public A
{
protected:
B(B const&) = default;
B& operator=(B const&) = default;
public:
B();
std::unique_ptr<A> clone() const override {
return std::unique_ptr<A>(new B(*this));
}
};
Deleting the copy assignment operator is probably a good idea, unless you need it.
Deleting operator=(const Base&) in Base is enough, as the implicitly declared copy assignment operator is defined as deleted for a derived class if the base class has no copy assignment operator (see cppreference.com).
If you really want copy assignment you can make the copy assignment operator virtual, and carefully implement the correct behaviour in the derived classes by
calling Base::operator= to assign the Base class members, and
assigning the members of the derived class, using dynamic_cast to ensure that the argument is of the correct type .
If done correctly, this avoids object slicing and retains the correct type.
An example (with copy constructor details omitted):
struct Point {
virtual Point& operator=(const Point& p) =default;
int x;
int y;
};
struct Point3d :public Point{
virtual Point3d& operator=(const Point& p);
int z;
};
Point3d& Point3d::operator=(const Point& p)
{
Point::operator=(p);
auto p3d = dynamic_cast<const Point3d*>(&p);
if(p3d){
z = p3d->z;
} else {
z = 0;
}
return *this;
}

How to make a C++ class produce non cloneable objects

How can I make a Class non-cloneable like we can do in Java while creating singleton.
Is there any condition we can put on copy constructor so that an exception can be thrown if user tries to make a copy of an object?
I am a novice in C++ so kindly add any info to this or redirect if an answer is already available for the same.
Just declare copy constructor and copy assign operator private
in C++03
class NonCopyable
{
public:
NonCopyable() { }
private:
NonCopyable(const NonCopyable&);
NonCopyable& operator=(const NonCopyable&);
};
Also you can make a class derive from NonCopyable, AnotherType is un-copyable
class AnotherNonCopyable : private NonCopyable
{
public:
AnotherNonCopyable () {}
}
With C++11:
class NonCopyableType
{
public:
NonCopyableType(const NonCopyableType&) = delete;
NonCopyableType& operator=(const NonCopyableType&) = delete;
};
You can delete the copy constructor and assignment operator:
struct Foo
{
Foo(const& Foo) = delete;
Foo& operator=(const Foo&) = delete;
};
If you don't have C++11 support, make them private, and don't implement them:
struct Foo
{
private:
Foo(const& Foo);
Foo& operator=(const Foo&);
};
Note In C++, class and struct are essentially the same.
Declare the copy-semantics as delete:
//C++11 only
MyClass(MyClass const &) = delete;
MyClass& operator=(MyClass const &) = delete;
That makes the class non-copyable!
//pre-C++11 code
private:
MyClass(MyClass const &); //declare only, don't define it
MyClass& operator=(MyClass const &); //declare only, don't define it
This should also work!
Is there any condition we can put on copy constructor so that an
exception can be thrown if user tries to make a copy of an object.
if you make the copy constructor private, The code will not compile when the programmer tries to make another copy. This is probably better than detecting the error with an exception at runtime.
class foo {
private:
operator = ( const foo& f);
};

calling operators of base class... safe?

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
}
}