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;
}
Related
Assume I want to suppress copying/moving in a base class, but allow it for a derived class. I can accomplish the functionality like this:
class Base {
public:
virtual ~Base() = default;
virtual bool magic() const;
protected:
Base() = default;
Base(const Base&) = default;
Base& operator=(const Base&) = default;
Base(Base&&) = default;
Base& operator=(Base&&) = default;
};
class Derived : public Base {
public:
Derived() = default;
Derived(int x, double y) : x_(x), y_(y) {};
bool magic() const;
private:
int x_;
double y_;
}
The "problem" is that this doesn't follow C.67 in the ISO C++ Core Guidelines:
https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Rc-copy-virtual
C.67 states
C.67: A polymorphic class should suppress public copy/move
Reason A polymorphic class is a class that defines or inherits at least one virtual function. It is likely that it will be used as a base class for other derived classes with polymorphic behavior. If it is accidentally passed by value, with the implicitly generated copy constructor and assignment, we risk slicing: only the base portion of a derived object will be copied, and the polymorphic behavior will be corrupted.
If the class has no data, =delete the copy/move functions. Otherwise, make them protected.
The way I interpret this is that I should delete my copy/move constructors/assignment operators in the base class, since it has no data members:
class Base {
public:
Base(const Base&) = delete;
Base& operator=(const Base&) = delete;
Base(Base&&) = delete;
Base& operator=(Base&&) = delete;
virtual ~Base() = default;
virtual bool magic() const;
protected:
Base() = default;
};
class Derived : public Base {
public:
Derived() = default;
Derived(int x, double y) : x_(x), y_(y) {};
Derived(const Derived& d) : Base(), x_(d.x_), y_(d.y_) {};
...
bool magic() const;
private:
int x_;
double y_;
}
But doing so, will force me to implement copy/move constructors/assignment operators in my derived class (which is a point with the guideline, I assume). I can come up with no other way than to manually copy all the data members of my derived class, which seems quite bloated. If I add a new data member and forget to update my copy/move functions, they will break.
Is there an easier way to define copy/move constructors/assignment operators in my derived class, when they are deleted in the base class? And what is the best practice in this situation?
EDIT:
As pointed out in the comments, the copy/move constructors/assignment operators should not be public in the derived class either, according to the same guideline. But the problem remains when defining them as protected.
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.
According to CppCoreGuideline, I should disable the copy constructor of a base class and propose a clone method: https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#Rc-copy-virtual
For example:
class B {
public:
explicit B() = default;
B(B&&) = default; // Move constructor
B& operator=(B&&) = default; // Move assignment operator
B(const B&) = delete; // Copy constructor
B& operator=(const B&) = delete; // Copy assignment
virtual ~B() = default;
virtual unique_ptr<B> clone()
{
return unique_ptr<B>{new B{*this}}; // how do this without copy constructor ?
}
private:
int c;
int d;
};
class D : public B {
public:
explicit D() = default;
D(D&&) = default; // Move constructor
D& operator=(D&&) = default; // Move assignment operator
D(const B&) = delete; // Copy constructor
D& operator=(const D&) = delete; // Copy assignment
virtual ~D() = default;
virtual unique_ptr<B> clone() override
{
// how can I copy all private data member of base class ???
}
};
but how can I copy all private data member in clone method? Obviously I'll use the CRTP pattern : C++: Deep copying a Base class pointer
I think the simplest way is to actually make the special members protected instead of deleted. This still prevents slicing, but makes it easier to implement clone(). Note that both the copy and move members need to be treated this way.
class B {
public:
// if this is truly intended to be a polymorphic base class, it probably
// doesn't make sense for the base to be able to clone itself.
virtual unique_ptr<B> clone() = 0;
protected:
B(B const& ) = default;
B& operator=(B const& ) = default;
private:
int c;
int d;
};
Which also allows the derived classes to do this easily:
class D : public B {
public:
D(D const& ) = default; // this is safe now
D& operator=(D const& ) = default;
unique_ptr<B> clone() override {
return unique_ptr<D>(new D(*this));
}
// ...
};
Rather than disabling the copy constructor, consider marking it protected. That way, clients of the class can't accidentally create a copy, but instances of the class can invoke the copy constructor as needed to implement the clone function. You can use the defaulted version of the copy constructor assuming you aren't doing any explicit resource management. Then, to implement clone, you can do something like this:
virtual unique_ptr<B> clone() override
{
return make_unique<D>(*this);
}
This invokes the object's own (protected) copy constructor, which in turn will invoke the base's (protected) copy constructor, etc.
As a note, there's no need to use CRTP here. Using good old fashioned copy constructors should be all you need.
I have the following MWE code:
#include <algorithm>
class Base{
public:
int baseMember;
friend void swap(Base& in, Base& out)
{
using std::swap;
swap(in.baseMember, out.baseMember);
}
virtual Base& operator=(Base obj)
{
swap(*this, obj);
return *this;
}
Base() : baseMember(1)
{
}
};
class Derived : public Base
{
public:
int derivedMember;
friend void swap(Derived& in, Derived& out)
{
using std::swap;
swap(in.derivedMember, out.derivedMember);
swap(static_cast<Base&>(in), static_cast<Base&>(out));
}
virtual Base& operator=(Base obj)
{
swap(*this, static_cast<Derived&>(obj));
return *this;
}
Derived() : Base(), derivedMember(2)
{
}
};
int main()
{
Base *b1 = new Derived();
Base *b2 = new Derived();
*b1 = *b2;
delete b1;
delete b2;
}
I have two Base pointers pointing to Derived data. I then do an assignment of the contents of the pointers. Since the Base class' assignment operator is virtual, polymorphism kicks in and the assignment operator of the Derived class (with the same signature) is called instead.
However, the static_cast to transform the source into a Derived object fails. Or, well, it successfully transforms it into a Derived object, but its derivedMember is garbage (after being initially set in the constructor).
How can this be avoided? How can an assignment between the Derived contents of two Base pointers be done?
Your code has a typo and inherently unsafe behavior. The typo is here:
virtual Base& operator=(Base obj) // <-- should be base&
{
swap(*this, static_cast<Derived&>(obj));
return *this;
}
If you fix this, your code should work in the simple example provided. But it would still fail at large, because how would you guarantee the argument passed to operator= will be in fact of Derived type?
Passing an argument by value slices that argument. So by the time your operator= function is called, you have an actual Base, not a Derived.
I'm afraid you'll need to overload your assignment for lvalues and rvalues.
virtual Base& operator=(const Base&);
virtual Base& operator=(Base&&);
I've got a class that inherits from a MSFT class, and therefore cannot be changed, and I'd like my derived class to have identical behavior for its copy constructor and copy assignment operator. The trouble I'm having is that in the copy constructor, you are free to invoke a constructor for the base class in the initializer list, but in the operator, this is not an option. How can I properly recreate this behavior in the assignment operator? Is it sufficient to just call the base class's constructor in the body of the operator overload?
Additional note: the base class inherits from CObject, which has operator=() and the copy constructor as private and unimplemented methods, so unfortunately any calls to those will result in a compile error.
I've provided a simplified code scenario below:
Class declarations:
class Base
{
protected:
int baseInt;
public:
Base(int);
}
class Derived : public Base
{
public:
Derived(const Derived& other);
Derived& operator=(const Derived& rhs);
private:
int derivedInt;
}
Derived class member functions:
// Copy Constructor
Derived(const Derived& other) : Base(5)
{
derivedInt = other.derivedInt;
}
// Copy Assignment Operator
Derived& operator=(const Derived& rhs)
{
if (&rhs != this)
{
derivedInt = other.derivedInt;
return *this;
}
}
EDIT: Updated syntax and added CObject note
In the general case, you do that either by explicitly calling operator= for the base class subobject, or by using the copy&swap idiom, if available:
//explicit call to base operator=
Derived& operator=(const Derived& rhs)
{
Base::operator=(rhs); //
derivedInt = rhs.derivedInt;
return *this;
}
//or copy&swap:
Derived& operator=(const Derived& rhs)
{
Derived tmp(rhs); //copy
tmp.swap(*this);
return *this;
}
void swap(Derived& other)
{
Base::swap(other);
std::swap(derivedInt,other.derivedInt);
}
Update: since your base class is not meant to be copy-assigned, your derived class should not be copy-assigned either. There are some cases where classes contain noncopyable parts but are perfectly copyable. In those cases the noncopyable part is something that does not directly contribute to the class objects' state, e.g. a unique ID. Then those parts will normally not be changed during assignment. However, in those cases the noncopyable parts should not be contained by inheritance but rather by aggregation.
Shortly said: Iheritance means "is-A" relationship. Your Base cannot be copied. Your Derived is a Base. Thus your Derived cannot be copied either.
Like this
Derived& operator=(const Derived& rhs)
{
Base::operator=(rhs);
derivedInt = rhs.derivedInt;
return *this;
}