Let the example be:
class Base {
Base (const Base & copyFrom) { globalRegister (* this); }
}
class Derived {
Derived (const Derived & copyFrom) : Base (copyFrom) {}
}
I've read suggestions to include the Base's copy constructor on the initialisation list of Derived in order to copy over the Base's properties (as in the example).
However, I have the Base's copy constructor passing itself (* this) to other object (to be registered with that object). Would that be a case where I actually must use (implicitly or explicitly) Base's (default) constructor on the initialisation list of Derived's copy constructor, and call the Base's copy constructor only in the body of Derived's copy constructor, when there is actually an object that can be attached by Base's copy constructor? Else - is (* this) a valid object?
Would that be a case where I actually must use (implicitly or explicitly) Base's (default) constructor on the initialisation list of Derived's copy constructor, and call the Base's copy constructor only in the body of Derived's copy constructor, when there is actually an object that can be attached by Base's copy constructor?
Why on earth would you want to do that?
(Oh, and you can not call a base class' copy constructor from a derived class' constructor's body. Only from its initialization list.)
Else - is (* this) a valid object?
The moment the base's initialization list has completed, all of base's members (and base classes) are fully constructed. The class itself, however, is only fully constructed when its constructor has finished.
More importantly, the derived class' constructor hasn't even started yet, so the object is not a derived class' object yet.
So whatever that registering function does, it has to take into account that the object's dynamic type is base
and that its constructor hasn't finished yet. (To be safe, all it can do is to store the object's address somewhere.)
Just for reference, the behavior is specified by § 12.7 2-3 of C++03:
2) To explicitly or implicitly convert a pointer (an lvalue) referring to an object of class X to a pointer (reference) to a direct or indirect base class B of X, the construction of X and the construction of all of its direct or indirect bases that directly or indirectly derive from B shall have started and the destruction of these classes shall not have completed, otherwise the conversion results in undefined behavior.
this
is a pointer to Derived
. In Base::Base()
, this
is implicitly cast to a Base*
, which is allowed because the construction of Derived has started and it has no other bases that derive from Base
.
§ 12.7 2 continues:
To form a pointer to (or access the value of) a direct nonstatic member of an object obj, the construction of obj shall have started and its destruction shall not have completed, otherwise the computation of the pointer value (or accessing the member value) results in undefined behavior.
Finally, § 12.7 3 is also important:
3) Member functions, including virtual functions (10.3), can be called during construction or destruction (12.6.2). When a virtual function is called directly or indirectly from a constructor (including from the mem-initializer for a data member) or from a destructor, and the object to which the call applies is the object under construction or destruction, the function called is the one defined in the constructor or destructor’s own class or in one of its bases, but not a function overriding it in a class derived from the con- structor or destructor’s class, or overriding it in one of the other base classes of the most derived object (1.8). If the virtual function call uses an explicit class member access (5.2.5) and the object-expression refers to the object under construction or destruction but its type is neither the constructor or destructor’s own class or one of its bases, the result of the call is undefined.
These two clauses means an instance of Derived
is a fully-fledged Base
once a Base
constructor begins, though it might be in an inconsistent state.