Reputation: 33
I am C++ newbie and i was trying to build a multiple inheritance class, with a common virtual inherited class( see code below). What my book politely ignored was the case where the virtual inherited class' constructor has parameters. The code below is my attempt to do that. If i remove the virtual inheritance of the class base in both derived class the code builds ok. However if i keep the way it is it won't build.( g++ under ubuntu)
I have several questions:
1) Least important first: why the code is not building?
2) Can virtual inherited classes have constructors with parameters?
3) If question 2) is true, then how the following line it is evaluated?
derived(int i,int j, int k):derived1(i,j),derived2(j,k){};
each derived class calls base constructor,each with second parameter. But only one copy of the base exist in derived since it is inherited as virtual. I want to understand in this case which base constructors is executed and with what parameter: j or k? ( i am not sure that this line is valid).
#include <iostream>
using namespace std;
class base
{
int x;
public:
base(int i){cout<<"Constructing base "<<i<<endl;x=i;}
~base(){cout<<"Destructing base"<<endl;}
};
class derived1:virtual public base
{
int x1;
public:
derived1(int i,int j): base(j){cout<<"Constructing derived1 " <<i<<endl;x1=i;}
~derived1(){cout<<"Destructing derived1"<<endl;}
};
class derived2:virtual public base
{
int x2;
public:
derived2(int i,int j): base(j){cout<<"Constructing derived2 "<<i<<endl;x2=i;}
~derived2(){cout<<"Destructing derived2"<<endl;}
};
class derived:public derived1, public derived2
{
int z;
public:
derived(int i,int j, int k):derived1(i,j),derived2(j,k){cout<<"Constructing derived "<<k<<endl;z=k;}
~derived(){cout<<"Destructing derived"<<endl;}
};
int main()
{
derived ob(2,3,4);
}
Upvotes: 1
Views: 235
Reputation: 171167
Any class in a hierarchy can list its virtual base classes among its mem-initializer-list (the part of the constructor definition after :
), but only one of them will actually execute these constructors: the most derived class.
Let's imagine a setup like this:
#include <iostream>
struct V
{
explicit V(char c) { std::cout << c << '\n'; }
};
struct A : virtual V
{
A() : V('a') {}
};
struct B : A, virtual V
{
B() : V('b') {}
};
struct C : B
{
C() : V('c') {}
};
int main()
{
A a; // prints 'a'
B b; // prints 'b'
C c; // prints 'c'
}
As you can see, even though all of A
, B
, and C
initialise V
in their constructor, only the most derived class (the type of the object you're actually creating) executes its initialiser.
Now let's imagine we add a class D
like this:
struct D : C
{
D() {}
};
This will not compile—D
does not list V
among its mem-initializer-list, so the default constructor is to be used, but that doesn't exist.
To address your concrete example with derived
derived from both derived1
and derived2
: when creating an object of type derived
, derived
is the most derived type therefore only the constructor of derived
itself can pass arguments to the base
constructor. Any initialisation of base
inside the constructors of derived1
and derived2
will simply be ignored when creating a derived
object.
Upvotes: 3
Reputation: 157414
Virtual base classes are initialized by the constructor of the most derived class, even if the most derived class does not inherit from them directly.
This means that if your class hierarchy includes any virtual base classes that require constructor parameters, the most derived class constructor has to name them in its mem-initializer-list and supply any required parameters at that point. Since virtual base classes are initialized before any non-virtual base classes, it's recommended to place them before non-virtual base classes in the initializer list:
derived(int i, int j, int k)
: base(i) // note: derived does not inherit directly from base
, derived1(i, j)
, derived2(j, k)
{
cout << "Constructing derived " << k << endl;
z = k;
}
This also means that if a derived class depends on particular parameters being passed to a virtual base, it should be marked final
so that it cannot be inherited from; otherwise, any inheriting class could pass the "wrong" parameters.
Note also that derived1
and derived2
have to supply constructor parameters for base
, even though they will never be used. This is a bit of a bug in the language, but no-one has bothered enough to clean it up yet (DR257 via https://stackoverflow.com/a/32214536/567292)
Upvotes: 2