If I have an abstract base class and I want to make all derived classes noncopyable and nonmovable is it sufficient to declare these special member functions deleted in the base class? I want to ensure that my entire class hierarchy is noncopyable and nonmovable and am wondering if I can get away with not having to declare those 4 special member functions as deleted in every derived class. I saw a SO answer where it seemed to imply that a derived class could explicitly declare a copy or move constructor despite being deleted from the base class but the following example results in a compilation error when I try to define a defaulted copy assignment operator so I'm unsure. This is the error:
derived_class.cc:15:15: error: defaulting this copy constructor would delete it after its first declaration DerivedClass::DerivedClass(const DerivedClass &) = default;
derived_class.h:9:22: note: copy constructor of 'DerivedClass' is implicitly deleted because base class 'virtual_functions::BaseClass' has a deleted copy constructor class DerivedClass : public BaseClass {
base_class.h:11:3: note: 'BaseClass' has been explicitly marked deleted here BaseClass(const BaseClass &) = delete;
// base_class.h
class BaseClass {
public:
BaseClass(const BaseClass &) = delete;
BaseClass(BaseClass &&) = delete;
BaseClass &operator=(const BaseClass &) = delete;
BaseClass &operator=(BaseClass &&) = delete;
virtual ~BaseClass() = default;
virtual bool doSomething() = 0;
protected:
BaseClass(std::string name);
private:
std::string name_;
};
// derived_class.h
class DerivedClass : public BaseClass {
public:
DerivedClass();
DerivedClass(const DerivedClass &);
bool doSomething() override;
};
// derived_class.cc
DerivedClass::DerivedClass(const DerivedClass &) = default;
If there is no copy constructor, c++ creates a default copy constructor which makes a shallow copy. If the object has no pointers to dynamically allocated memory then shallow copy will do.
While the copy constructor is used to set up a new version of an object that's a duplicate of another object, the assignment operator is used to overwrite the value of an already-created object with the contents of another class instance.
The copy constructor and copy-assignment operator are public but deleted. It is a compile-time error to define or call a deleted function. The intent is clear to anyone who understands =default and =delete . You don't have to understand the rules for automatic generation of special member functions.
The Copy constructor and the assignment operators are used to initializing one object to another object. The main difference between them is that the copy constructor creates a separate memory block for the new object. But the assignment operator does not make new memory space.
You cannot prevent a child class from defining its own copy/move constructor. That said, it will prevent it "out of the box", meaning if you do not provide one, or use a inline default constructor, it will also be marked as deleted. The reason you get a error here when you try to just define the constructor as default is because you are not allowed to do that in an out of line definition when a member or base has implicitly deleted it. Had you used
class DerivedClass : public BaseClass {
public:
DerivedClass(const DerivedClass &) = default;
bool doSomething() override;
};
then the code would compile, and you would only get an error if you actually try to call the copy constructor. This works because an inline implicit default is allowed even when a member or base implicitly deletes it and the end result is the constructor is implicitly deleted.
Is deleting copy and move constructors/assignment operators in base class enough?
It is enough to prevent implicitly generated copy and move constructors/ assignment operators.
I saw a SO answer where it seemed to imply that a derived class could explicitly declare a copy or move constructor despite being deleted from the base class
This is correct. You cannot prevent this. Well, you can prevent this by declaring the class final. Then there cannot be derived classes, and thus derived classes cannot be copyable.
Of course, such explicitly declared copy constructor (and other) will not be able to copy the base sub object that is non-copyable. The constructors must use BaseClass(std::string)
and the assignment operators cannot modify the state of the base object in any way (unless they use some trick to get around access specifier encapsulation).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With