Suppose I have these two classes
class base_size
{
public:
int size()
{ return 5; }
};
class base_implement
{
public:
base_implement(int s) : _vec(s)
{
cout << "size : " << _vec.size() << endl;
}
private:
vector<float> _vec;
};
If I were to inherit from both of these would it be alright to call one of these classes member function in the other's constructor? For example
class derived :
public base_implement,
public base_size
{
public:
derived() : base_size(), base_implement(size())
{
// Is this OK?
// If derived is not yet constructed can I access this->size() ?
// Works in VC++. Not sure about other compilers.
}
};
In principle that's fine. The base subobjects and the member objects are constructed before the derived constructor body runs, so you can call the member function without problems. You can even call your own member functions in the constructor; you just have to make sure that they don't rely on anything that comes later in on the same constructor.
Just make sure to call the base initializers in the correct order, i.e. the order of their declaration, and/or fix your class definition: For base_size::size()
you want the base_size
subobject to be fully constructed, so it has to come first.
class derived : base_size, base_implement
{
derived() : base_size(), base_implement(size()) { /* ... */ }
// ...
};
You can safely call an inherited member function from the initialization list provided that nothing of what that function does depends on how the member data up to that level of inheritance have been initialized. And, since size()
doesn't rely on any member data and all what it does is just returning a literal
int size()
{ return 5; }
your code would work with any compiler. So there is even no need to have base_size()
in the initialization list
derived() : base_size(), base_implement(size())
in this case.
However, switching to a more realistic example, where base_size
has a constructor that initializes an instance variable (i.e. member data), it would make more sense having base_size()
in the initialization list:
class base_size
{
public:
base_size ()
{ _size = 5; } // initialization
int size()
{ return _size; }
private:
int _size; // instance variable
};
class base_implement
{
public:
base_implement(int s) : _vec(s)
{
cout << "size : " << _vec.size() << endl;
}
private:
vector<float> _vec;
};
class derived :
public base_implement,
public base_size
{
public:
derived() : base_size(), base_implement(size())
{
// crash
}
};
And, with this specific example, the program would crash because vector
wouldn't receive a valid value to allocate its size. And the the reason would be the order of base classes you have in the so-called base-specifier-list:
public base_implement,
public base_size
Referring to an authority, this is what is stated by the standard in section 12.6.2, "Initializing bases and members"
Initialization shall proceed in the following order:
- First, and only for the constructor of the most derived class as described below, virtual base classes shall be initialized in the order they appear on a depth-first left-to-right traversal of the directed acyclic graph of base classes, where “left-to-right” is the order of appearance of the base class names in the derived class base-specifier-list.
- Then, direct base classes shall be initialized in declaration order as they appear in the base-specifier-list (regardless of the order of the mem-initializers).
- Then, nonstatic data members shall be initialized in the order they were declared in the class definition (again regardless of the order of the mem-initializers).
- Finally, the body of the constructor is executed.
So if you were to replace
public base_implement,
public base_size
with
public base_size,
public base_implement
it all would initialize properly and the program would run fine with most standard-compliant compilers. For one thing, with MSVC 10.0 for sure as I've just tested.
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