Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Calling a base member in constructor in multiple inheritance in C++

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.
   }
};
like image 685
Iam Avatar asked Jul 15 '12 14:07

Iam


2 Answers

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()) { /* ... */ }
     // ...
 };
like image 122
Kerrek SB Avatar answered Nov 04 '22 21:11

Kerrek SB


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.

like image 45
Desmond Hume Avatar answered Nov 04 '22 22:11

Desmond Hume