Copy construct and assignment operator in the derived class - c++

I have a class (class A) which inherits another class (class B).
class A: public B
Class B disabled copy construct and assignment operator (due to not allow a copy).
private:
B(const B&);
B& operator=(const B&);
My question is that should I also disable copy construct and assignment operator in the derived class as well or is it okay if I did not define both.

Subclasses should have the same or stricter [preconditions, post conditions and invariants] than their parent classes. This is the Liskov Substitution Principle. So, you should not re-enable copy construction etc/whatever in the derived class, since you will be loosening the contract of the base class.
If you find you need to do it (or would really like to do it), then it may be a sign that you need to rethink your design.

The question is rather, should you re-enable it. If any base or member is noncopyable, your class will be noncopyable by default. Generally, you won't want to remove it, because it will be difficult or impossible to give it reasonable semantics. But there are notable exceptions: if the base class is abstract, for example, you may want to enable the copy constructor (but not assignment) in the derived class in order to support cloning.

Disallowing copy-constructor and assignment operator of base class will cause that the copy-constructor and assignment operator of derived class won't be usable as well:
class B {
public:
B() { }
private:
B(const B&);
B& operator=(const B&);
};
class A : public B { };
In this case you don't need to explicitly disallow these for derived class, since the default implementation will have to use the parent's implementation first. So if you don't try to access these in your code:
int main() {
A a;
}
it will be perfectly valid. However if you try to copy:
int main() {
A a;
A a2 = A(a);
}
compiler will complain about class A trying to access private members of B (however semantically the second scenario shouldn't happen).

Related

copy constructor on base class

List C.67 in the cpp core guideline says: A base class should suppress copying, and provide a virtual clone instead if "copying" is desired.
If the copy constructor is defined as deleted in the base, then the move operations are also suppressed for base class and all derived classes.
On the other hand, move operations may improve performance. My question is what would be realistic approach we should adopt when we design a class hierarchy?
Suppose we have the following class hierarchy? how should we design A and B to properly support copy and move operations.
class A{
public:
A(const std::string& as) = deleted;
//should we define other copy/move operators?
virtual void foo();//
virtual ~A();//
private:
std::string s;
};
class B: public A{
public:
//how do we define copy/move operators?
void foo() override;
~B() override;
private:
std::vector<std::string> vs;
};
Confusions
Copy vs. clone
First, note that clone is not “copy for objects of polymorphic type”: they are simply different operations with different semantics.
Copy (via a copy constructor) means “create an object of statically specified type with the value of another”. (Recall that constructors cannot be virtual, for want of a current object whose class could provide the behavior.) The user must specify the type, and should expect that the copied object is “reinterpreted” (sliced) as the known, specified type if it is in fact of a derived type.
clone copies an object of a dynamically known derived class as another object of that class. Since the argument determines the type of the result, the user cannot specify it and indeed does not (statically) know what is chosen. (Heap allocation is a corollary.)
Which one you want depends on what lifetime and type you want the result to have (including “the same type as that one” as a choice). I find it puzzling that someone would write a copy (e.g., a by-value parameter whose specified type is a concrete base class) and be surprised by what they had chosen.
Virtual assignment
Next, note that an abstract class need not fear slicing except on assignment (which must be via a reference). Assignment can be virtual (since an object already exists), but it can’t statically avoid slicing because the types need not match:
struct B {
virtual ~B()=default;
virtual B& operator=(const B&)=default;
// ...
};
struct D1 : B {
D& operator=(const B&) override;
// ...
};
struct D2 : B {/* ... */};
void f() {
B &&b=D1();
b=D2(); // ok
}
The assignment must use just the common B part, or… throw? If the assignment can fail, it’d be clearer to provide it as a function: perhaps bool assign_like(const B&) &; that returns false if the types differ.
Protection
So we indeed must do something about at least assignment if we want to avoid the risk of slicing. The Core Guidelines idea of deleting the assignment operator is reasonable, but I would just make it protected in each abstract base class.
Concrete leaves only
If you never inherit from a concrete class, that’s all you need to prevent slicing: the implicit (public) special member functions in the leaf classes do not slice, automatically use the base’s corresponding SMFs, and can in turn be used automatically as appropriate. (For example, the concrete classes may then be passed by value.)
“Deep” hierarchy
In a concrete base class, you have two choices for each SMF:
Make it protected anyway, denying the possibility of deliberately copying an object (even if the source’s complete object type is known statically).
Leave it available and send any confused users of the class hierarchy here to learn about the difference between copy and clone (and the impossibility of a “fully virtual” assignment).

Calling an assignment operator for one of bases with vtables of multiple-inherited derived class in C++

Ok, it's going to a bit a bit tricky. Here is a (simplified) code:
class A
{
virtual ~A();
// fields, none of which has an assignment operator or copy constructor
};
class B
{
virtual ~B();
// same as A
};
class Derived : public A, public B
{
Derived();
Derived(const B& b);
// no fields
};
With Derived::Derived(const B& b) (i.e. accepting one of it's bases) as follows
Derived::Derived(const B& b)
{
*static_cast<B*>(this) = b;
// Do other stuff with protected fields declared in B
}
For me it's something in line of "just avoid doing this way", but that's an existing code and we are experiencing a subtle memory corruption suspiciously near this code. So, I'm curious if that's ok.
The curious part here is that both base classes have vtables and none of them has any explicit copy/assignment constructors/operators.
From my understanding, memory layout for a Derived class is as follows
`Derived`
---------
A-vtable
A-fields
B-vtable
B-fields
And when I'm calling a virtual function, declared in "B" I'm using B-vtable and when I'm calling a virtual function, declared in "A" I'm using A-vtable, i.e. vtables are not merged together.
And from my understanding implicit copy/assignment constructor/operator for B should only affect B-fields, when it's called as *static_cast<B*>(this) = b; (static_cast should give a pointer to start of B-fields, with B-vtable residing at a negative offset from it, AFAIK).
So, from my understanding, this code is perfectly safe and correct, though unclear and hacky, but safe. Am I correct? Are there any compiler-specific quirks I should be aware of (we are talking about MSVC 2012 here)?
Edit: Guys, I know about copy constructor/assignment operator difference, thank you very much. It was one of 3 occurrences that was talking about just copy constructor, because I've oversaw it, and now every answer spends half of the text to tell the completely unrelevant to the question difference.
Yes, it is correct. There is this peculiar behavior of casting a derived class to one of its parents. When multiple inheritance occurs, like in your case, the actual address of the pointer can change. To use your exampe:
this -> | A-vtable |
| A-fields |
static_cast<B*>(this) -> | B-vtable |
| B-fields |
The same change of pointer in this happens when you call a function derived from B on a Derived object.
However, be aware that copy constructor is not what you invoke in the line:
*static_cast<B*>(this) = b;
Instead, you are invoking an assignment operator of B, i.e. B::operator=(const B& other). If you didn't define one, the default assignment operator is used. The equal sign is treated as a copy constructor only in the context of variable declaration:
B newObj = b;
If you want to implement your own copy constructor for the Derived and then explicitly invoke the parent copy constructor of B, try this instead:
Derived::Derived(const B& b) : B(b)
{
}
Why don't you just call constructor of base class?
Derived::Derived(const B& b)
: B(b)
{
}
Similar thing can be done for assignment operators:
Deriver& Derived::operator=(const Derived& rhs)
{
A::operator=(rhs); //Assign A's part
B::operator=(rhs); //Assign B's part
//Derived-specific assignments
return *this;
}
This the preferred, safe and totally valid way to initialize objects of classes, that inherit from something.
Also, in this line:
*static_cast<B*>(this) = b;
you call assignment operator, not copy constructor. Since you wrote, that neither of base types has them defined, default (generated by the compiler) is used.
I don't understand why are you even thinking about vtables. This is simple inheritance from classes with compiler-generated copy constructors and assignment operators. Everything is simple and can be done without any hacks :)

Can a derived class be made uncopyable by declaring copy constructor/operator private in base class?

I thought in theory the answer to this question was yes.
However, in practice, my compiler (VS2010) does not seem to complain in the following situation: I have an abstract base class providing some common interface (yet having no data members) and various sub and subsubclasses derived from it.
class Base
{
public:
Base() {}
virtual ~Base() {}
virtual void interfaceFunction1() = 0;
virtual void interfaceFunction2() = 0;
private:
Base(const Base&); // all derived classes should be uncopyable
Base& operator=(const Base&);
// no data members
};
My compiler found it unproblematic to even implement full copy constructors in sub- or subsubclasses.
How can I make sure that every class derived from Base is uncopyable?
edit: If I understand well, this is exactly what Scott Meyers explained in item 6 of Effective C++ (3rd edition, 2005) with his idea of the class Uncopyable (only extended here to a full interface class). What is the difference that makes his idea work ? (I know that he inherits privately, but this should not pose a problem)
This should prevent the compiler from generating a copy constructor for derived classes which do not declare one explicitly. However, nothing prevents a derived class from explicitly declaring a copy constructor which will do something else than call the copy constructor of Base.
There is no way to make sure derived classes are instantiable but not copyable.
Rather than declaring the copy constructor/operator as private declare them as deleted. Declaring copy constructor/operator as private is not the best solution to making the derived classes non-copyable. If you want the base class to be completely non-copyable then declare the copy constructor/operator as deleted as copy can still take place inside the member functions of Base as private members are accessible to that class's functions. You can use the C++11 feature of delete:
Base(const Base&) = delete; // copy constructor
Base& operator=(const Base&) = delete; // copy-assignment operator
But declaring copy constructor/operator as private is also right as long as you're aware that copy can still take place inside the member functions of Base.
In C++11 and later there is the option to declare a constructor deleted.
struct X {
X( const X& ) = delete;
};
Now, anything derived from X that rely on the copy-constructor will not compile.
This is most useful when you want to avoid problems because the compiler auto-generates constructors...

Forbid users to create objects but allow some classes

I have the following two classes.
Class A
{
proctected:
A(){}
};
Class B
{
push_new_A_into_v();
vector<A> v;
};
The function
push_new_A_into_v();
will not compile since A's constructor is protected. To make B inherit from A will not help since the method create a completely new A(Why is protected constructor raising an error this this code?).
The reason A's constructor is protected is to make users unable to create an object of type A.
How can I make it possible for the method to work while users is still unable to create objects of type A?
In addition to user2913094's answer:
If giving B full friendship just to allow construction seems unacceptable, you can add a constructor that requires a construction token, which can only be obtained by B:
class A {
public:
class ConstructionToken {
private:
ConstructionToken();
friend class B;
};
A(ConstructionToken const&);
protected:
A();
};
Note that the token class is completely empty, but since only B can access its private constructor, that essentially prevents the user of invoking A's public constructor directly.
This allows for more fine-grained access control, but has the disadvantage that it requires introducing an additional constructor overload on A.
class A {
friend class B;
...

C++ Qt Reflection with Copy and Assignment

As the QObject documentation and many others explain, a QObject has an identity and thus hides its copy constructor and assignment operator.
However, I'm not deriving from QObject for its dynamic properties feature or the signals/slots feature. I only want reflection, or the ability to access Foo::staticMetaObject.
class Foo : public QObject {
Q_OBJECT
Q_ENUMS(Color)
public:
enum Color { Blue, Red, Pink };
private:
Color color;
};
Q_DECLARE_METATYPE(Foo::Color)
I then can't copy Foo with:
Foo a;
Foo b;
a = b;
What's the best way to allow copy and assignment in this case? Do I absolutely need to write a copy constructor and assignment operator? What would they look like? Will reflection still work?
If you are only interested in having reflection for
the class name,
enums and flags (Q_ENUMS, Q_FLAGS),
class info (Q_CLASSINFO),
you can use Q_GADGET instead of Q_OBJECT:
class Foo {
Q_GADGET
Q_ENUMS(Color)
public:
enum Color { Blue, Red, Pink };
private:
Color color;
};
which will declare and define Foo::staticMetaObject.
You can certainly implement both a copy constructor and a copy assignment operator in your derived class, but it's likely a sign of bad design. Take this example:
#include <iostream>
class Base {
public:
Base() {}
private:
Base(const Base& other) {
std::cout << "Base copy constructor invoked!" << std::endl;
}
};
class Derived : public Base {
public:
Derived() {}
Derived(const Derived& other) {
std::cout << "Derived copy constructor invoked!" << std::endl;
}
};
int main(int argc, char** argv) {
Derived a;
Derived b = a;
return 0;
}
This will compile just fine. However, as expected, when you run the resulting program, all that is printed is Derived copy constructor invoked!. When the base class declares its copy constructor/copy assignment operator as private, that doesn't prevent derived classes from implementing their own versions. It simply prevents derived classes from calling the base class versions.
And therein lies the problem: it's always good practice to make sure you copy all parts of an object, so that you indeed have two distinct copies. Part of your object includes the data owned by the base class, so you should always make sure to invoke the base class's copy constructor/copy assignment operator to ensure that a full copy is made. But that data is by design non-copyable. Thus, it is impossible to copy all parts of the object.
It's up to you if you want to stick with this design. One important thing to ask yourself is, does your derived class really need to be copyable at all? If not, then there's nothing to worry about!
I don't know much about qt, but if the copy constructor is not allowed then there should be a reason for it (which is discussed in the link you posted). You can change your design not to have it.
Still if you insist then memcpy can be your last resort. I don't recommend it personally, because you have to take care about deep copying, vtable etc. which are not always trivial.