With virtual inheritance it is the most derived object's obligation to initialize not only direct base clases, but all virtual ancestor classes as well.
As an example, consider the following class hierarchy (minding that structs use public inheritance by default):
struct B {
B() = delete;
B(int);
};
struct D1 : virtual B {
D1(int x) : B(x) {}
};
struct D2 : virtual B {
D2(int x) : B(x) {}
};
If I were to intoduce a class D12 like this...
struct D12 : D1, D2 {
D12(int x) : D1(x), D2(x) {}
};
Compilation would fail, as D12 has to initialize B and because it doesn't do that, the compiler assumes that the default constructor (which is deleted) is supposed to be invoked. The solution would be to manually invoke the B::B(int) constructor:
struct D12 : D1, D2 {
D12(int x) : B(x), D1(x), D2(x) {}
};
I see a problem in that solution. The problem is that the declaration of class D1 does not contain either B or virtual. To properly implement this class we must somehow know that the classes D1 and D2 inherit from a virtual base class B. One could say that it should be assumed that if we use multiple inheritance than we will have to initialize some virtual base classes. However the problem persists when only seemingly using regular inheritance:
struct D : D1 {
// D still has to initialize B
D(int x) : B(x), D1(x) {}
};
As a sort of workaround I thought that maybe I could make D also inherit from B (virtually) to at least express this hidden dependency in the declaration:
struct D : virtual B, D1 {
D(int x) : B(x), D1(x) {}
};
Is this guaranteed to be equivalent to the previous code? Is there a better solution to this problem?
Related
Why can't I do this?
class A
{
public:
int a, b;
};
class B : public A
{
B() : A(), a(0), b(0)
{
}
};
You can't initialize a and b in B because they are not members of B. They are members of A, therefore only A can initialize them. You can make them public, then do assignment in B, but that is not a recommended option since it would destroy encapsulation. Instead, create a constructor in A to allow B (or any subclass of A) to initialize them:
class A
{
protected:
A(int a, int b) : a(a), b(b) {} // Accessible to derived classes
// Change "protected" to "public" to allow others to instantiate A.
private:
int a, b; // Keep these variables private in A
};
class B : public A
{
public:
B() : A(0, 0) // Calls A's constructor, initializing a and b in A to 0.
{
}
};
Leaving aside the fact that they are private, since a and b are members of A, they are meant to be initialized by A's constructors, not by some other class's constructors (derived or not).
Try:
class A
{
int a, b;
protected: // or public:
A(int a, int b): a(a), b(b) {}
};
class B : public A
{
B() : A(0, 0) {}
};
Somehow, no one listed the simplest way:
class A
{
public:
int a, b;
};
class B : public A
{
B()
{
a = 0;
b = 0;
}
};
You can't access base members in the initializer list, but the constructor itself, just as any other member method, may access public and protected members of the base class.
# include<stdio.h>
# include<iostream>
# include<conio.h>
using namespace std;
class Base{
public:
Base(int i, float f, double d): i(i), f(f), d(d)
{
}
virtual void Show()=0;
protected:
int i;
float f;
double d;
};
class Derived: public Base{
public:
Derived(int i, float f, double d): Base( i, f, d)
{
}
void Show()
{
cout<< "int i = "<<i<<endl<<"float f = "<<f<<endl <<"double d = "<<d<<endl;
}
};
int main(){
Base * b = new Derived(10, 1.2, 3.89);
b->Show();
return 0;
}
It's a working example in case you want to initialize the Base class data members present in the Derived class object, whereas you want to push these values interfacing via Derived class constructor call.
Why can't you do it? Because the language doesn't allow you to initializa a base class' members in the derived class' initializer list.
How can you get this done? Like this:
class A
{
public:
A(int a, int b) : a_(a), b_(b) {};
int a_, b_;
};
class B : public A
{
public:
B() : A(0,0)
{
}
};
While this is usefull in rare cases (if that was not the case, the language would've allowed it directly), take a look at the Base from Member idiom. It's not a code free solution, you'd have to add an extra layer of inheritance, but it gets the job done. To avoid boilerplate code you could use boost's implementation
Aggregate classes, like A in your example(*), must have their members public, and have no user-defined constructors. They are intialized with initializer list, e.g. A a {0,0}; or in your case B() : A({0,0}){}. The members of base aggregate class cannot be individually initialized in the constructor of the derived class.
(*) To be precise, as it was correctly mentioned, original class A is not an aggregate due to private non-static members
I end up with a multiple inheritance diamond problem with the situation that there is no default constructor in the base class.
struct A {
A(int x) {}
};
struct B : virtual public A {
using A::A;
};
struct C : virtual public A {
using A::A;
};
struct D : virtual public B, public C {
D(int x) : B(x), C(x) {}
};
int main() {
D d(1);
}
The compiler is complaining that:
error: constructor for 'D' must explicitly initialize the base
class 'A' which does not have a default constructor
D(int x) : B(x), C(x) {}
But, I don't really have access to A from D... How can I fix that? Thanks
You do have an access to A since you're inheriting from it.
You would have to call the constructor of A in order to construct the object D.
Your D constructor should look something like that :
D(int x) : A(x), B(x), C(x) {}
Because of the multiple inheritance, constructors of B and C would ignore the A(x) part and you will have only one object which is D.
This does not seem to work in C++11:
class B : public A
{
public:
B(const A& a)
: A(a) // parent constructor for passing the parameter
, B() // delegating constructor for init of other members
{};
// ...
};
gcc tells me that an initializer for a delegating constructor must appear alone.
How do I both call the constructor of the parent class with the parameter, and call the basic constructor of the B class? (I have a bunch of other constructors in B that need the same behavior).
Right now I am considering writing a private B::init() function and use it in all constructor bodies, but that tastes a bit much of C++03.
What is the preferred solution?
I believe the preferred way of delegation is the other way around, it's not meant to be used to refactor common parts of constructors, but rather to define the simpler one as a special case of a the more complex case.
So you should start with B(const A& a) and use this as delegation target.
class B : public A
{
public:
B() : B(A());
B(const A& a) : A(a) // parent constructor for passing the parameter
{};
};
You are calling A() anyways when creating B.
The rationale behind it is when you have two "partially specialized" c'tors, you wouldn't be able to use them to initialize the complex one. E.g:
class B : public A
{
public:
B() {};
B(int) : B() {};
B(double) : B() {};
B(double,int) : B(int), B(double) {}; // can't do it.
};
I believe the technical reason is explained in Bathsheba's answer. Look what would happen if you had a common part in B():
class B : public A
{
public:
B() {};
B(int) : B() {};
B(double) : B() {};
B(double,int) : B(int), B(double) {}; //ooops would get B() called twice!
};
It's the diamond problem known from inheritance. The solution is to reverse the logic.
class B : public A
{
public:
B() : B(0,0) {};
B(int a) : B(a,0) {};
B(double d) : B(0,d) {};
B(double a, int d) {/*full implementation*/};
};
B(const A& a) : A(a), B() does not make sense since B() will also initialise the base class A. That would essentially be a duplicate initialisation, which is an inherent contradiction.
The only real option for the language is to disallow anything else if you use a delegated constructor. That's what your compiler is telling you.
I am trying to understand better the concept of virtual inheritance, and what are its perils.
I read in another post (Why is Default constructor called in virtual inheritance?) that it (= virtual inheritance) changes the order of constructor call (the "grandmother" is called first, while without virtual inheritance it doesn't).
So I tried the following to see that I got the idea (VS2013):
#define tracefunc printf(__FUNCTION__); printf("\r\n")
struct A
{
A(){ tracefunc; }
};
struct B1 : public A
{
B1(){ tracefunc; };
};
struct B2 : virtual public A
{
B2() { tracefunc; };
};
struct C1 : public B1
{
C1() { tracefunc; };
};
struct C2 : virtual public B2
{
C2() { tracefunc; };
};
int _tmain(int argc, _TCHAR* argv[])
{
A* pa1 = new C1();
A* pa2 = new C2();
}
The output is:
A::A
B1::B1
C1::C1
A::A
B2::B2
C2::C2
Which is not what I expected (I expected the order of the 2 classes will be different).
What am I missing? Can someone explain or direct me to a source that explains it better?
Thanks!
In your example, your output is to be expected. Virtual inheritance comes into play in the instance when you have a class with multiple inheritance who's parent classes also inherit from the same class/type (i.e. the "diamond problem"). In your example, your classes might be set up to virtually inherit (if needed elsewhere in the code), but they don't necessarily 'virtually inherit' based on your example since none of the derived classes (B1/B2/C1/C2) do more than inherit directly from A.
To expand, I've tweaked your example to explain a little more:
#include <cstdio>
#define tracefunc printf(__FUNCTION__); printf("\r\n")
struct A
{
A() { tracefunc; }
virtual void write() { tracefunc; }
virtual void read() { tracefunc; }
};
struct B1 : public A
{
B1() { tracefunc; };
void read(){ tracefunc; }
};
struct C1 : public A
{
C1() { tracefunc; };
void write(){ tracefunc; }
};
struct B2 : virtual public A
{
B2() { tracefunc; };
void read(){ tracefunc; }
};
struct C2 : virtual public A
{
C2() { tracefunc; };
void write(){ tracefunc; }
};
// Z1 inherits from B1 and C1, both of which inherit from A; when a call is made to any
// of the base function (i.e. A::read or A::write) from the derived class, the call is
// ambiguous since B1 and C1 both have a 'copy' (i.e. vtable) for the A parent class.
struct Z1 : public B1, public C1
{
Z1() { tracefunc; }
};
// Z2 inherits from B2 and C2, both of which inherit from A virtually; note that Z2 doesn't
// need to inherit virtually from B2 or C2. Since B2 and C2 both virtual inherit from A, when
// they are constructed, only 1 copy of the base A class is made and the vtable pointer info
// is "shared" between the 2 base objects (B2 and C2), and the calls are no longer ambiguous
struct Z2 : public B2, public C2
{
Z2() { tracefunc; }
};
int _tmain(int argc, _TCHAR* argv[])
{
// gets 2 "copies" of the 'A' base since 'B1' and 'C1' don't virtually inherit from 'A'
Z1 z1;
// gets only 1 "copy" of 'A' base since 'B2' and 'C2' virtualy inherit from 'A' and thus "share" the vtable pointer to the 'A' base
Z2 z2;
z1.write(); // ambiguous call to write (which one is it .. B1::write() (since B1 inherits from A) or A::write() ?)
z1.read(); // ambiguous call to read (which one is it .. C1::read() (since C1 inherits from A) or A::read() ?)
z2.write(); // not ambiguous: z2.write() calls C2::write() since it's "virtually mapped" to/from A::write()
z2.read(); // not ambiguous: z2.read() calls B2::read() since it's "virtually mapped" to/from A::read()
return 0;
}
While it might be "obvious" to us humans which call we intend to make in the case of the z1 variable, since B1 doesn't have a write method, I would "expect" the compiler to choose the C1::write method, but due to how the memory mapping of objects work, it presents a problem since the base copy of A in the C1 object might have different information (pointers/references/handles) than the copy of the A base in the B1 object (since there's technically 2 copies of the A base); thus a call to B1::read() { this->write(); } could give unexpected behaviour (though not undefined).
The virtual keyword on a base class specifier makes it explicit that other classes that virtually inherit from the same base type, shall only get 1 copy of the base type.
Note that the above code should fail to compile with compiler errors explaining the ambiguous calls for the z1 object. If you comment out the z1.write(); and z1.read(); lines the output (for me at least) is the following:
A::A
B1::B1
A::A
C1::C1
Z1::Z1
A::A
B2::B2
C2::C2
Z2::Z2
C2::write
B2::read
Note the 2 calls to the A ctor (A::A) before Z1 is constructed, while Z2 only has 1 call to the A constructor.
I recommend reading the following on virtual inheritance as it goes more in depth on some of the other pitfalls to take note of (like the fact that virtually inherited classes need to use the initialization list to make base class ctor calls, or that you should avoid using C-style casts when doing such a type of inheritance).
It also explains a little more to what you were initially alluding to with the constructor/destructor ordering, and more specifically how the ordering is done when using multiple virtual inheritance.
Hope that can help clear things up a bit.
The output of compiler is right. In fact, this is about the goal of virtual inheritance. Virtual inheritance is aimed to solve the 'Diamond problem' in the multiple inheritance. For example, B inherits from A, C inherits from A and D inherits from B, C. The diagram is like this:
A
| |
B C
| |
D
So, D has two instances A from B and C. If A has virtual functions, there is a problem.
For example:
struct A
{
virtual void foo(){__builtin_printf("A");}
virtual void bar(){}
};
struct B : A
{
virtual void foo(){__builtin_printf("B");}
};
struct C : A
{
virtual void bar(){}
};
struct D : B, C
{
};
int main()
{
D d;
d.foo(); // Error
}
If I use my xlC compiler to compile and run:
xlC -+ a.C
The error message is like that:
a.C:25:7: error: member 'foo' found in multiple base classes of different types
d.foo(); // Error
^
a.C:9:18: note: member found by ambiguous name lookup
virtual void foo(){__builtin_printf("B");}
^
a.C:3:18: note: member found by ambiguous name lookup
virtual void foo(){__builtin_printf("A");}
^
1 error generated.
Error while processing a.C.
The error message is very clear, member 'foo' found in multiple base classes of different types. If we add virtual inheritance, the problem is solved. Because the construction rights of A is handled by D, there is one instance of A.
Back to your code, the inheritance diagram is like this:
A A
| |
B1 B2
| |
C1 C2
There is no 'diamond problem', this is only single inheritance. So, the construction order is also A->B2->C2, there is no difference of output.
You won't be able to see any difference in the output because the output will be same in any of the following class hiearchies:
Hiearchy 1
class A {};
class B2 : virtual public A {};
class C2 : virtual public B2 {};
Hiearchy 2
class A {};
class B2 : public A {};
class C2 : virtual public B2 {};
Hiearchy 3
class A {};
class B2 : virtual public A {};
class C2 : public B2 {};
Hiearchy 3
class A {};
class B2 : public A {};
class C2 : public B2 {};
In all these case, A::A() will be executed first, followed by B2::B2(), and then C2::C2().
The difference between them is when does A::A() get called. Does it get called from B2::B2(), or C2::C2()?
I am not 100% clear on the answer for Hiearchy 1. I think B2::B2() should get called from C2::C2 since B2 is a virtual base class of C. A::A() should get called from B2:B2() since A is a virtual base class of B2. But I could be wrong on the exact order.
In Hierarchy 2, A::A() will be called from B2::B2(). Since B2 is the virtual base class of C2, B2::B2() gets called from C2::C2(). Since A is a normal base class of B2, A::A() gets called from B2::B2().
In Hierarchy 2, A::A() will be called from C2::C2(). Since A is a virtual base class, A::A() gets called from C2::C2(). B2::B2() gets called after the call to A::A() is completed.
In Hierarchy 4, A::A() will be called from B2::B2(). I think this case needs no explanation.
To clarify my doubt regarding Hiearchy 1, I used the following program:
#include <iostream>
class A
{
public:
A(char const *from) { std::cout << "Called from : " << from << std::endl; }
};
class B2 : virtual public A
{
public:
B2() : A("B2::B2()") {}
};
class C2 : virtual public B2
{
public:
C2() : A("C2::C2()") {}
};
int main()
{
C2 c;
}
I got the following output:
Called from : C2::C2()
This confirms what #T.C indicated in his comment, which is different from what I had expected. A::A() gets called from C2::C2, not from B2::B2.
The following code illustrates my problem:
struct Base {
Base(int n) : n(n) {}
virtual ~Base() = 0;
int n;
};
Base::~Base() {}
struct A : public virtual Base {
A(int n) : Base(n) {}
virtual ~A() = 0;
};
A::~A() {}
struct B : public virtual Base {
B(int n) : Base(n) {}
virtual ~B() = 0;
};
B::~B() {}
struct Test : public virtual A, public virtual B {
Test(int n) : Base(n), A(n), B(n) {} // how to avoid this duplication?
};
int main() {
Test c(0);
(void)c;
}
As you can see, the Test constructor must initialize Base, A and B explicitly. Is this normal? Or is there way to avoid the redundancy?
#include <assert.h>
struct Base {
Base(int n) : n(n) {}
virtual ~Base() = 0;
int n;
protected:
Base() { assert( false ); }
};
Base::~Base() {}
struct A : public virtual Base {
virtual ~A() = 0;
};
A::~A() {}
struct B : public virtual Base {
virtual ~B() = 0;
};
B::~B() {}
struct Test : public virtual A, public virtual B {
Test(int n) : Base(n) {} // how to avoid this duplication?
};
int main() {
Test c(0);
(void)c;
}
For one, there is no need for Test to derived virtually from A and B in your scenario, since neither A nor B seem to be used as base lasses.
And, yes, Base must be initialized in the most derived class. The reason is simply that the immediate base classes of test share the same Base sub-object. In order to do that, it must be constructed by the most-derived class before either of them is constructed.
Personally, I always thought that A or B could construct it as well, with which does it determined by declaration order as base class, and screw the other's constructor. But that would enable very subtle bugs, when both call different constructors, and the subtle issue of base class declaration order could introduce surprising behavior changes. (Not that we wouldn't have such issues elsewhere in the language, but one less spot is maybe a good thing.)
However, note that, while C++ gives you all the freedom you can possibly get, it's usually best if virtual base classes are abstract classes, have no member data, and thus only a default constructor. Since this will be called implicitly, none of the derived classes will have to bother with calling a constructor explicitly.
There's no redundant initialization. Constructors of virtual base classes are called only once, by the most derived class's constructor.
Update The question can be interpreted to mean two things, (1) how to avoid redundant calls to the constructor at runtime and (2) how to avoid writing redundant initialization lists. Apparently the author means (2). I was answering (1).