My question is simple, is the next code safe?
struct Parent {
B* _a;
Parent(B* a) : _a(a) {}
};
struct Child : public Parent {
B _b;
Child() : Parent(&_b), _b(2){};
};
int main() {
Child c;
return 0;
}
Two more points:
_b
will be allocated (and its memory address) and that this code will work regardless of which compiler I use.Thanks in advance.
clarification
by safe I actually meant that the memory address was valid, since I already knew It was not initialized.
other notes
In my actual code I wanted to store the object of type B
as a pointer to its base class A
, like this:
struct Parent {
A* _a;
Parent(A* a) : _a(a) {}
};
struct Child : public Parent {
B _b;
Child() : Parent(&_b), _b(2){};
};
int main() {
Child c;
return 0;
}
Which, if I understand AndreyT answer correctly, is illegal. I guess I'll try to do this differently, since this approach was error prone. (I might forget that I couldn't use that pointer and do something with it in my next refactor).
Conclusion: All other things being equal, your code will run faster if you use initialization lists rather than assignment. Note: There is no performance difference if the type of x_ is some built-in/intrinsic type, such as int or char* or float .
Initializer List is used in initializing the data members of a class. The list of members to be initialized is indicated with constructor as a comma-separated list followed by a colon. Following is an example that uses the initializer list to initialize x and y of Point class.
Const member variables must be initialized. A member initialization list can also be used to initialize members that are classes. When variable b is constructed, the B(int) constructor is called with value 5. Before the body of the constructor executes, m_a is initialized, calling the A(int) constructor with value 4.
The initializer list is used to directly initialize data members of a class. An initializer list starts after the constructor name and its parameters.
In the sense that you describe, yes it is safe: the memory is allocated and it is perfectly fine to pass that pointer to the parent. The memory for Child::_b
is actually integral part of the memory of the entire Child
. It doesn't require any explicit additional "allocations". By the moment the Child::Child
constructor is invoked, the memory is obviously already there.
However, the memory the pointer points to can only be used in number of limited ways (the standard describes in 3.8 what can and what cannot be done with it), since the object it points to has not been initialized yet. In your specific example you simply store the pointer. That is perfectly OK.
But if you, for example, wanted to convert that pointer to some base class type (assuming for a second that B
has some base class), you code would be illegal. It is illegal to convert pointers to uninitialized objects to their base class pointers (again, see 3.8).
The order of initialization is as follows:
// Base classes:
Parent(&_b)
// Members:
_b(2)
// Constructor Body:
Child() { }
Whether this is safe depends on your definition of "safe." By your definition ("will it work?"), yes, it is safe. The lifetime of Child::_b
begins when the Child
object is created, so you can obtain a pointer to it and that pointer refers to an object. However, you can't use the pointed-to value until after _b
is initialized, which is after the constructor of the base class, Parent
, has returned.
The parent constructor will be called first and you will be passing the address of an unitiliazed member variable to that constructor. It's not safe.
edit:
I think AndreyT stated more clearly and more vividly the type of problems that sprang to mind when I wrote my answer. Those are the types of errors that aren't immediately perceivable. The kind that will keep you up at night attempting to figure out where the dangling pointer exists in your code or where the memory corruption is occurring.
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