Constructor call hierarchy - c++

I'm having a tricky time with calling rules for constructors in a type hierarchy. Here is what I do:
class A{
protected:
int _i;
public:
A(){i = 0;}
A(int i) : _i(i){}
virtual ~A(){}
virtual void print(){std::cout<<i<<std::endl;}
};
class B : virtual public A{
protected:
int _j;
public:
B() : A(){_j = 0;}
B(int i, int j) : A(i), _j(j){}
virtual ~B(){}
virtual void print(){std::cout<<i<<", "<<j<<std::endl;}
};
class C : virtual public B{
protected:
int _k;
public:
C() : B(){_k = 0;}
C(int i, int j, int k} : B(i,j), _k(k){}
virtual ~C(){}
virtual void print(){std::cout<<i<<", "<<j<<", "<<k<<std::endl;}
};
int main(){
C* myC = new C(1,2,3);
myC->print();
delete myC;
return 0;
}
Now, I would like to have new C(1,2,3) call the constructor of B(1,2) which then in turn should call the constructor A(1) to store _i=1, _j=2, _k=3. When creating the instance myC of the class C, for some reason I don't understand, however, the first constructor to be called is the standard constructor of A, i.e., A::A(); This obviously leads to wrong results, as the protected variable _i is assigned the value 0. The constructor A(1) is never called. Why is this so? I find this very counter intuitive. Is there some way to avoid explicitly calling all constructors within the type-hierarchy to achieve the desired behavior?
Thx for the help!

do you really need a virtual inheritance here?
you've got a problem because a very first virtual base ctor will be called first, but you don't specify any when inherit C from B (latter already have A virtually inherited, so default was called).
one solution is to remove virtual inheritance... as mentioned in the answer of Arne Mertz.
another (if you really want virtual inheritance) is to call A explicitly from C ctor:
C(int i, int j, int k} : A(i), B(i,j), _k(k){}

When you use virtual inheritance, the most derived class must call the constructors for all its virtual bases directly. In this case, the constructor for C must call the constructors for B and A. Since you only call the B constructor, it uses the default A constructor. It does not matter that the B constructor calls another A constructor: since it is a virtual base class, this call is ignored.
You have two ways around this problem: explicitly call the A(int) constructor:
C(int i, int j, int k} : A (i), B(i,j), _k(k){}
or use normal inheritance instead of virtual.

This is because you used virtual inheritance, which makes only sense in the presence of multiple inheritances. Just inherit normally, and all will be as you expect it.

Why do you declare virtual inheritance? If you remove virtual keyword from class B: virtual public A { ... then your code will work fine. By declaring virtual A, C will call A() directly. If you remove virtual then C will not call A().

Related

How to initialize derived class member in case of multiple inheritance [duplicate]

Is possible to have virtual inheritance for class not providing default constructor?
The present diamond diagram (the simplest one with the only change of no default constructor provided) does not compile (g++ 4.4.3).
class A {
public:
A(int ) {}
};
class B : virtual public A {
public:
B(int i) : A(i) {}
};
class C : virtual public A {
public:
C(int i) : A(i) {}
};
class D : public B, public C {
public:
D(int i) : B(i), C(i) {}
};
Thanks,
Francesco
You need to call A's constructor explicitly here
D(int i) : A(i), B(i), C(i) {}
virtual base classes are special in that they are initialized by the most derived class and not by any intermediate base classes that inherits from the virtual base. Which of the potential multiple initializers would the correct choice for initializing the one base?
If the most derived class being constructed does not list it in its member initalization list then the virtual base class is initialized with its default constructor which must exist and be accessible.
Shamelessly copied from here :-)
I believe your class D also needs to explicitly call A's constructor in its initializer list.
The Dr. Dobbs article Multiple Inheritance Considered Useful explains various ways of dealing with this. The recommendation is basically to provide default constructors and init() methods. It adds more work for B and C but prevents D from having to know about A.
you need explict call A's construct like this:
D(int i) : A(i), B(i), C(i) {}

store abstract member, keep interface simple

I know that it is not possible to have an instance of an abstract class as a base member of another class, i.e.,
#include <iostream>
class Base {
public:
Base() {};
virtual ~Base() {};
virtual int yield() = 0;
};
class C1: public Base {
public:
C1(): Base() {};
virtual ~C1() {};
virtual int yield() {return 1;};
};
class D {
public:
D(Base & b): b_(b) {};
virtual ~D() {};
private:
Base b_;
}
int main() {
C1 c;
D d(c);
}
will fail to compile with the error
test.cpp:22:10: error: cannot declare field ‘D::b_’ to be of abstract type ‘Base’
The obvious workaround is to use (shared) pointers instead. This, however, makes main somewhat more complicated to read,
int main() {
auto c = std::make_shared<C1>();
D d(c);
}
which I would really like to avoid.
Is there a way to keep the main file as simple as in the above example and still achieve the desired functionality?
You can't. When you are creating D it allocates (in heap or in stack) memory for B. And C1 class needs size of base class B plus size of extra variables/etc in C1 itself even if there are nothing new.
So, use pointers instead.
The error caused by virtual int yield() = 0;. If you use virtual int yield(), it will works. When you used virtual int yield() = 0;, it said that the function is a pure virtual function, so you must override it. So you should give its inheritance class and use the instance of inheritance class in class C1. In a world, virtual int yield() = 0; only remind you that it is only a interface, you must override it. I hope this can help you.
Since Base is an abstract class (has at least one pure virtual function), it can't be instantiated directly.
When you declare D's class member as "Base b_", you are effectively trying to create an instance. You can instead use a pointer there (or some kind of safe/smart pointer).
#include <iostream>
class Base {
public:
Base() {};
virtual ~Base() {};
virtual int yield() = 0;
};
class C1: public Base {
public:
C1(): Base() {};
virtual ~C1() {};
virtual int yield() {return 1;};
};
class D {
public:
D(Base * b): b_(b) {};
virtual ~D() {};
private:
Base *b_; // Use a pointer or safe ptr or something of that sort.
}
int main() {
C1 c;
D d(&c);
}
No. One of the properties of an abstract class is that it cannot be instantiated. That means an instance of an abstract class cannot be a member of another class.
Even if Base was not abstract, your class D's constructor would be slicing the object passed. If passed an instance of C1, the copying (in the initialiser list of D's constructor) would not magically cause an instance of D to contain an object of type C. It would instead create a copy only of the Base part of that object.
In short, your design is broken, and will not work even if - syntactically - it would be possible to simplify the code in main().

Inhereting constructors in C++ [duplicate]

This question already has an answer here:
Why is Default constructor called in virtual inheritance?
(1 answer)
Closed 9 years ago.
C++11 standard provides a way to inherit constructors from the base class. My question is regarding earlier standards. Suppose I inherit constructors in the following way:
class Base {
public:
Base() {};
Base(int b) { a = ++b;}
virtual int foo() {return 0;}
int a;
};
class A : public virtual Base {
public:
A() {}
A(int b): Base(b) {}
int foo() {return a*a;}
};
class C : public A {
public:
C() {}
C(int b ): A(b) {}
int foo() { return (a*a + a);}
};
Note that I am having virtual inheritance of the Base class. Now when I try to initialize a pointer to an object of type C then instead of calling Base(b) constructor, the code ends up calling Base() constructor. Here is the main function that I used:
int main(){
C *c = new C(5);
std::cout << c->Base::a << std::endl;
}
The output value of "a" is 0. However, when I remove the virtual keyword while inheriting the Base class, then Base(b) constructor is called and the value of "a" is 6. Can someone help me in understanding what is going on? Why is it that with virtual inheritance default constructor is called?
Virtual base classes are initialised based on the member initialiser list of the constructor of the most derived class.
In your case, when you're creating an instance of C, its Base subobject will be initialised based on the member-initialiser list in C's constructor; and since Base is not listed there, it will be default-initialised.
If you were creating an instance of A, then indeed A's member-initialiser list would be used.
So to call the Base constructor which you want, you'd have to modify C's constructor like this:
C(int b ): A(b), Base(b) {}

Can I avoid the redundant base class initializations when using virtual inheritance?

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).

Diamond sub-problem: non-multiple inheritance in side branch still require class constructor

Strange problem occurred when I tried to "solve" usual diamond problem in a usual way - using virtual inheritance:
A
/ \* both virtual
B C
\ /
D
However my base class A doesn't have default constructor, so I was to call it manually from D. However when I try to add a class E into this diamond as C-inherited
A
/ \* both virtual
B C
\ / \
D E
it is still needed to call constructor of A in E constructor manually, i.e. C doesn't what to create A from E even though there is neither multiple inheritance nor diamond A-C-E.
class A
{public:
A (int _N): N(_N) {};
void show()
{cout<<"A"<<N;}
protected:
int N;
};
class B: public virtual A
{ public:
B(int n): A(2*n) {};
void show()
{ cout<<"B"<<N;}
};
class C: public virtual A
{ public:
C(int n): A(3*n) {};
void show()
{ cout<<"C"<<N;}
};
class D: public B,C
{ public:
D(): B(1), C(2), A(3) {};
void show()
{ cout<<"D"<<N;}
};
class E: public virtual C
{ public:
E(): C(1) {};
void show()
{ cout<<"E"<<N;}
};
int main()
{D d; // OK
A *a = &d;
a->show();
E e; // NOT OK, no function A::A() to call in E::E()
A *a2 = &e;
a2->show();
return 0;
}
Is it possible to solve this issue without calling constructor of A from E? I need C to do it properly :-).
Or is it possible not to try to solve diamond problem at all:
A A
| | no virtual at all
B C
\ / \
D E
and still try to declare object of class D with two instances of A but telling compiler to use A of C when colling from D each time? When I try to add
using C::A
into the declaration of D it still produce error of unambiguous base A.
Is it possible to solve this issue without calling constructor of A from E? I need C to do it properly :-).
The constructor for the most derived class (in this case, E) is responsible for calling the constructor for any virtual base classes.
The constructor of C cannot call the constructor of A because C is not the most derived class. Virtual base classes are initialized before any direct base classes, so E must initialize by A before it can initialize C.
Yes, virtual base class constructor calls are unlike virtual function overriding:
you can override a virtual function in a derived class;
you must override a virtual function only if the function is overridden in one of the base classes but not in others;
you cannot not override the init-list for virtual base classes of the base class constructor.
This implies that:
as far as virtual function overriding is concerned, virtual inheritance does not affect single inheritance at all;
but when it comes to base class constructor calls, virtual inheritance affects every derived class.