Initialization of a base class reference from a derived class member - c++

I have two classes, Base and Derived. Derived constructs Base using its own member object, which inherits from Base::BaseChild.
struct Base
{
struct BaseChild
{
int x = 5;
};
Base(BaseChild& c): baseData(c), constantVar(c.x)
{
assert(constantVar == 5);
}
int getX() const {return baseData.x;}
private:
const int constantVar;
BaseChild& baseData;
};
struct Derived: public Base
{
struct DerivedChild: public BaseChild
{
double y = 4.0;
};
Derived(): Base(data) {}
private:
DerivedChild data;
};
Derived myObject;
assert(myObject.getX() == 5);
Reasoning: I do this in this way because everything seems pretty encapsulated for my case, where I need to send Childs to swap their content (vector, shared_ptr, unique_ptr) with other Childs, keeping child memory address, and I still can access to Child object from base class without the need of a virtual function, which was killing my app performance.
Question: I've read another post like this one, where it states initialization of a Derived member before the Base isn't possible. So the constantVar assert would always fail. However getX() works fine, after the constructor, and I'm interested in these functions which are called once the constructor ends. Is this safe? Or is there any hidden danger here?

The base class Base of Derived is constructed before the member data.
As a result data will not be initialized when you pass a reference to it to Base's constructor. The initialization will happen after that constructor call.
You are however trying to read the member x of data in Base's constructor. At this point data's lifetime has not started yet and accessing the value of a non-static data member of an object outside its lifetime causes undefined behavior.
Whether or not the assertions succeed isn't significant. Undefined behavior allows for either outcome.
The situation would be potentially different (although technically not rules in the standard) if you were not trying to access the value of data inside Base's constructor, but only storing the reference to it.

Related

Pass derived class members to base class constructor

I have the following piece of code that is working but I don't understand why it works (inspired by a real life code base):
Base class definition:
class Pointers {
private:
int* Obj1;
double* Obj2;
public:
Pointers(int* Obj1_, double* Obj2_) : Obj1{Obj1_}, Obj2{Obj2_} {}
};
We now derive a class from our base class where we shadow the two pointers with an int and a double of the same name:
class Objects : public Pointers {
public:
int Obj1{69};
double Obj2{72};
Objects() : Pointers(&Obj1, &Obj2) {}
};
Within the constructor of Objects we call the constructor of Pointers and pass the adresses of Obj1 and Obj2. This actually works: The (shadowed) pointers will point to Obj1 (69)and Obj2 (72).
My question is: Why is this working? I thought that in the first step the base class members are constructed (which are the two pointers) and only after that the derived class members (the int and double with the same name) are constructed. How can we pass these objects adresses to the base class constructor in the first place?
It works, because at the moment when you call your base class constructor, the derived class members
int Obj1{69};
double Obj2{72};
are not yet initialized, but their addresses are already known, and you are using their addresses to initialize pointers in the base class. Note that it has little to do with shadowing.
Of course if you try to print the pointed values in Pointers constructor
Pointers(int* Obj1_, double* Obj2_) : Obj1{Obj1_}, Obj2{Obj2_} {
std::cout << "Obj1: " << *Obj1 << ", Obj2: " << *Obj2 << std::endl;
}
you will get some garbage (UB), because pointed to values are not yet initialized. To make the code more clear avoid member fields assignments in the class body, rather use the constructor member initializer list:
Objects() : Pointers(&Obj1, &Obj2), Obj1{69}, Obj2{72} {}
The initialization of the class actually proceeds as follows:
Determine the memory that's used to construct the object (e.g. stack location or use of the result of the new operator).
Enter call of Object::Object()
Pointers::Pointers(int* Obj1_, double* Obj2_) gets called. This happens before any initializers for the members of Object are executed. The memory locations are already available though (see step 1.), so the address-of operator can be used.
Initialization of Pointers part of the object completes
The member variables of Object are initialized.
Note simply that simply passing the address of member variables can be done, but you need to make sure the pointers are not dereferenced in the base class constructor, since this would result in undefined behaviour.
Here:
class Objects : public Pointers {
public:
int Obj1{69};
double Obj2{72};
Objects() : Pointers(&Obj1, &Obj2) {}
};
Obj1 refers to Objects::Obj1. If there is a same named member in Pointers then it is shadowed by the member in Objects. You need to qualify the name Pointers::Obj1. You don't do that, so Obj1 refers to Objects::Obj1. Nothing surprising here.
Then here:
class Pointers {
private:
int* Obj1;
double* Obj2;
public:
Pointers(int* Obj1_, double* Obj2_) : Obj1{Obj1_}, Obj2{Obj2_} {}
};
Nothing is shadowed, Obj1_ and Obj1 are distinct identifiers. The base class initializes its members with the pointers you pass to the constructor.
This seems to indicate that when the ctor of Pointers is called within the ctor of Objects it is already known at which adresses in memory the members of the derived class will be?
Yes. You can use addresses and references of members of objects that are under construction, before those members are initialized. As long as you do not dereference the pointer (or try to read the member's value via the reference) all is fine. For example the code would invoke undefined behavior, if in the base constructors body you would add some std::cout << *Obj1;, because the int pointed to by Obj is not initialized at that point.

Why C++ doesn't allow derived class to use base class member in initialization list?

I've got following program:
#include<iostream>
using namespace std;
struct Base01{
int m;
Base01():m(2){}
void p(){cout<<m<<endl;}
};
struct Derived01:public Base01{
Derived01():m(3){}
};
struct Derived02:virtual public Base01{
Derived01():m(4){}
};
struct my: Derived01,Derived02{
my():m(5){}
};
int main(){
return 0;
}
Both gcc/clang reports compilation error.
I just wish to know what's the language design consideration here, why derived class can only call base class ctor in initialization list, but cannot use base class members directly?
What you do in the constructor initializer list is initialization. It is something that has to be done only once in the lifetime of the object. In general case, that's what starts the objects lifetime.
Base class's constructor (which finished working before your derived class's constructor-proper became active) has already initialized all direct subobjects of the base class. It has already started their lifetimes. If you attempt to reach-in and initialize a direct subobject of base class from the derived class's constructor, that will obviously be the second initialization of the same object. This is completely unacceptable in C++. The language design generally does not allow you to initialize something the second time.
In your case the subobject in question has fundamental type int, so it is hard to see the harm in such "re-initialization". But consider something less trivial, like an std::string object. How do you suggest the derived class should "undo and redo" the initialization already performed by the base class? And while formally it is possible to do it properly, constructor initializer lists are not intended for that purpose.
In general case doing something like that would require a language feature that would allow user to tell base class's constructor something along the lines of "please, leave this subobject of yours uninitialized, I will reach-in and initialize it later from the derived class". However, C++ does not provide users with such capability. A vaguely similar feature exists in virtual base class initialization, but it serves a very specific (and different) purpose.
The proper way to do this in C++ is to pass that value to the base class constructor. Your Base01 class needs an additional constructor that takes the desired value for m. Something like this:
struct Base01{
int m;
Base01():m(2){}
// Added this:
Base01(int mVal) : m(mVal) {}
void p(){cout<<m<<endl;}
};
struct Derived01:public Base01{
Derived01() : Base01(3) {} // Calling base constructor rather than
// initializing base member
};
struct Derived02:virtual public Base01{
Derived01() : Base01(4){} // Same here
};
struct my: Derived01,Derived02{
my(): Base01(5){} // And here.
};
As AnT said, you can't initialize twice--but you can set it up so that things are initialized the way you want in the first place by doing as above.
You certainly can use a base class member in the ctor-initializer list:
struct Base
{
int x;
Base(int x) : x(x) {}
};
struct Derived
{
int y;
Derived() : Base(7), y(x) {}
}
Here, the base member x appears in the initializer for the derived member y; its value will be used.
AnT has done a very nice job explaining why the ctor-initializer list can't be used to (re-)initialize members of base subobjects.
The fundamental language design considerations in this are separation of concerns (avoiding making a base class depend on its derived classes), and that a base class is responsible for initialising its own members (and any bases it has).
A related consideration is that members of the base class don't exist - as far as the derived class constructor is concerned - before the base class constructor completes. If an initialiser list of a derived class was able to reach in and initialise a base class member, then there are two possible consequences
If the base class constructor has not been invoked, its members will not exist when the derived class tries to initialise them.
If the base class constructor has been invoked, the member has been initialised. Initialisation (as distinct from assignment to reinitialise) happens once in the lifetime of an object, so it does not make sense to initialise it again.
Neither of these possibilities really make sense in practice, unless the base class is poorly designed (e.g. its constructors do not properly initialise its members). The sort of machinery needed so they might make sense (e.g. changing order of construction of base classes in a hierarchy, dependent on what member a derived class is trying to initialise) would make compiler machinery more complicated (e.g. being able to both control and track the order of construction of base class members, in case a derived class should choose to reach in), and also mean that the order of construction of classes would depend on derived classs). This would introduce a dependency of the base class behaviour (the means by which it is initialised) on derived classes.
The simpler means is for the base class to provide a constructor that properly initialised the member in question, and for the derived class constructor to invoke that base class constructor in its initialiser list. All of the above (hypothetical) considerations then go away.

Is it OK to pass member object in base class initialiser list?

I have two classes where one is a base class containing a pointer to a member object on a derived class.
Like this:
class Bar { };
class Foo : Bar { };
class A
{
public:
A(Foo *foo) { this->foo = foo };
private:
Foo *foo;
}
class B : public A
{
public:
B() : A(&bar) { };
private:
Bar bar;
}
My question is: is B.bar guaranteed to be allocated before being passed as an initialisation parameter to the constructor of A?
Put another way: if I create an instance of B is B->foo guaranteed to be a valid pointer to an instance of a Bar?
The base subobject is constructed before the member objects. Pointers and references to all the members are valid, but you must not access the actual objects until they are constructed.
Keep in mind that while the particular example works (object is allocated so taking addresses of its members works), it is not a universally valid pattern (which I just learned hard way).
There is implicit cast B* to A*, which in this case is trivial, in case of having virtual methods and more complex objects can introduce offset (it can be executed on uninitialized memory), and in case of virtual inheritance would cause SegFault - if B inherited A virtually and as implementation detail B contained pointer to A and allocated it dynamically).

Post constructor initialization

I have a set of objects derived from common base, ApiObject. I need to be able to register all ApiObjects in a separate data structure, but I need to have an actual address of the object being created, not the base class (I'm using multiple inheritance).
I can't put the code to register an object in ApiObject constructor, because it does not know the address of the derived object; nor can I put it in the derived classes' constructors, because we have no way of knowing whether we are actually constructing another derived class (e.g. if class B is inherited from A, and both can be constructed).
So the only option I see is to explicitly call the registration function every time we create an object, as in
B* b = new B(...);
RegisterObject(b);
However, this doesn't seem to be a very good solution, as I have to remember to call this function every time.
I suppose I should give more context to explain why I'm doing this. The objects are created via an overloaded new operator, and it needs the object to know the context it was created in (Lua state). E.g.
Foo* object = new(L) Foo(...);
// Foo is derived from ApiObject, and we want ApiObject to have a reference to L
Currently it is done in a somewhat unelegant way - the new operator allocates additional bytes before the object and stores the L pointer in there, along with some additional data to describe the object type. The base class then receives a pointer to this 'metadata' via the init function.
Otherwise, the first thing that comes to mind are virtual functions, but they can't be called from the constructor, so I'd have to register the base ApiObject pointer but only call the virtual function at some later point, and I'm not sure that's prettier than my current implementation.
What is the type required for RegisterObject? If it takes a
Base*, then you can call it from the constructor of Base,
regardless of the final hierarchy. If it takes some other type,
then you want to call it from the constructor of that type; you
do not want to call it from all classes derived from Base,
but only for those derived from whatever type it takes.
If RegisterObject takes a Base*, and you call it from a
function in a derived class, the first thing that will occur is
that the pointer you pass it will be converted to a Base*.
RegisterObject never receives a pointer to the derived object,
only to the Base in the derived object.
You can additionaly derieve every object you want to register from CRTP class, which performs registration, e.g.
template<class T>
struct registarar_t<T>
{
registarar_t()
{
register(derived());
}
T* derieved()
{
return static_cast<T*>(this);
}
}
struct IWantToRegister : registrar_t<IWantToRegister>, ApiObject
{
}
Also, be careful, derived() pointer is right, but object is not yet initialized (accessing it in parent constructor)
Maybe kassak's solution is more elegant, I'm not that advanced, but I'd recommend something like this (register shoudl be called in the constructor so you don't have to write it every time:
#include <iostream>
struct ApiObject;
void registerObj(ApiObject *foo);
struct ApiObject{
public:
ApiObject(std::string n){
name = n;
registerObj(this);
}
std::string name;
};
void registerObj(ApiObject *foo){
std::cout<<"register called on "<<foo->name<<"\n";
}
struct A : public ApiObject{
public:
A(std::string n) : ApiObject(n) {
std::cout<<"init A\n";
}
};
struct B : public ApiObject{
public:
B(std::string n) : ApiObject(n) {
std::cout<<"init B\n";
}
};
int main(){
B *b = new B("b obj");
A *a = new A("a obj");
delete b;
delete a;
}
You can call the registration function from the base constructor. Just make the base destructor virtual. The address will be same for base and derived class. Just don't use the pointer address before the whole object is created.
Once all the objects are fully created, the pointer address can be safely used through virtual functions or dynamic-casted to derived class.

C++ allocate objects on heap of base class with protected constructors via inheritance

I have a class with protected constructor:
class B {
protected:
B(){};
};
Now I derive from it and define two static functions and I manage to actually create objects of the class B, but not on the heap:
class A : public B {
public:
static B createOnStack() {return B();}
//static B* createOnHeap() {return new B;} //Compile time Error on VS2010
};
B b = A::createOnStack(); //This works on VS2010!
The question is: 1) Is VS2010 wrong in allowing the first case? 2) Is it possible to create objects of B without modifying B in any way (no friendship and no extra functions).
I am asking, because it is possible to make something similar when dealing with instances of B and its member functions, see:
http://accu.org/index.php/journals/296
Thank you in advance for any suggestion!
Kind regards
Yes, this code is non-compliant. This is related to special rules for protected member access (C++03 draft, 11.5/1):
When a friend or a member function of a derived class references a protected nonstatic member function or
protected nonstatic data member of a base class, an access check applies in addition to those described earlier in clause 11.10). Except when forming a pointer to member (5.3.1), the access must be through a pointer to, reference to, or object of the derived class itself (or any class derived from that class) (5.2.5).
When you use B() or new B(), you're effectively using the constructor through a pointer to the base class.
You can create an object of type A (I assume that A is as posted - no additional members/non-static functions) and use it instead. If you're creating it on stack, everything should work fine, unless you're trying to assign other objects of type B to it. If you're creating it on heap, everything is fine as long as B's destructor is virtual. If B's destructor is not virtual, and you're returning new A() as a B*, then deleting the pointer is technically undefined behavior (5.3.5/3:
In the first alternative (delete object), if the static type of the operand is different from its dynamic type, the static type shall be a base class of the operand’s dynamic type and the static type shall have a virtual destructor or the behavior is undefined.
However you'll probably find it working fine in practice, so you can rely on the actual behavior if there is no other workaround (i.e. use it as a last resort).
There is a common misunderstanding on what protected actually means. It means that the derived class can access that particular member on itself not on other objects. The compiler should have rejected both functions as in both cases it is accessing the constructor of an object that is not of the derived type.
Another example, easier to discuss for its correctness would be:
struct base {
protected:
int x;
};
struct derived : base{
static void modify( base& b ) {
b.x = 5; // error
}
};
The commented line is an error as it is trying to modify an object of type base, not necessarily a derived object. If the language allowed that code to compile, then you would be able to modify an object of type base or even objects of types derived1, derived2... effectively breaking access rules.
struct derived2 : base {};
int main() {
base b;
derived2 d;
derived::modify( b ); // modifying a base!!!
derived::modify( d ); // modifying a derived2!!!
}