In the next code, building a C class starts with initializing A then B1 and then B2 and at last C class. However, when initializing B1 and B2, the debugger ignored the initialization of A(1) and A(2) (that appear on the intialization list of B1 and B2 in this order) but didn't ignore the initialization of B2(3) at C initialization list.
Why is that?
Thanks in advance.
Here is the code:
struct A {
int i;
A() { i = 0; }
A(int _i) : i(_i) {}
virtual void f() { cout << i; }
};
struct B1 : virtual A {
B1() : A(1) { f(); }
void f() { cout << i+10; }
};
struct B2 : virtual A {
B2(int i) : A(2) { f(); }
};
struct C : B1, B2 {
C() : B2(3) {}
};
int _tmain(int argc, _TCHAR* argv[])
{
C* c = new C();
return 0;
}
For virtual bases, the most derived class has to provide the constructor arguments, e.g.:
struct C
: B1, B2 {
C()
: A(17)
, B2(3) {}
};
If the most derived class does not mention the virtual base in its initializer list, the default constructor of the virtual base is used (if there is none, it is an error).
The reason this is done is to avoid ambiguity of which of the derived classes should provide the constructor arguments for the shared virtual base: the most derived class knows best what is actually needed.
That's exactly how compiler avoids "diamond of death". If A(1) and A(2) would be called, means there was base class duplicate initialization , this is the purpose of virtual inheritance - remove ambiguous common base elements.
Related
While working with templates I ran into a need to make a base class constructors accessible from inherited classes for object creation to decrease copy/paste operations.
I was thinking to do this through using keyword in same manner with functions case, but that not work.
class A
{
public:
A(int val) {}
};
class B : public A
{
};
class C : public A
{
public:
C(const string &val) {}
};
class D : public A
{
public:
D(const string &val) {}
using A::A; // g++ error: A::A names constructor
};
void main()
{
B b(10); // Ok. (A::A constructor is not overlapped)
C c(10); // error: no matching function to call to 'C::C(int)'
}
So my question: Is there any way to import a base class constructors after new ones in inherited class been declared?
Or there is only one alternative to declare new constructors and call a base ones from initializer list?
Yes, Since C++11:
struct B2 {
B2(int = 13, int = 42);
};
struct D2 : B2 {
using B2::B2;
// The set of inherited constructors is
// 1. B2(const B2&)
// 2. B2(B2&&)
// 3. B2(int = 13, int = 42)
// 4. B2(int = 13)
// 5. B2()
// D2 has the following constructors:
// 1. D2()
// 2. D2(const D2&)
// 3. D2(D2&&)
// 4. D2(int, int) <- inherited
// 5. D2(int) <- inherited
};
For additional information see http://en.cppreference.com/w/cpp/language/using_declaration
Prefer initialization:
class C : public A
{
public:
C(const string &val) : A(anInt) {}
};
In C++11, you can use inheriting constructors (which has the syntax seen in your example D).
Update: Inheriting Constructors have been available in GCC since version 4.8.
If you don't find initialization appealing (e.g. due to the number of possibilities in your actual case), then you might favor this approach for some TMP constructs:
class A
{
public:
A() {}
virtual ~A() {}
void init(int) { std::cout << "A\n"; }
};
class B : public A
{
public:
B() : A() {}
void init(int) { std::cout << "B\n"; }
};
class C : public A
{
public:
C() : A() {}
void init(int) { std::cout << "C\n"; }
};
class D : public A
{
public:
D() : A() {}
using A::init;
void init(const std::string& s) { std::cout << "D -> " << s << "\n"; }
};
int main()
{
B b; b.init(10);
C c; c.init(10);
D d; d.init(10); d.init("a");
return 0;
}
No, that's not how it is done. Normal way to initialize the base class is in the initialization list :
class A
{
public:
A(int val) {}
};
class B : public A
{
public:
B( int v) : A( v )
{
}
};
void main()
{
B b(10);
}
You'll need to declare constructors in each of the derived classes, and then call the base class constructor from the initializer list:
class D : public A
{
public:
D(const string &val) : A(0) {}
D( int val ) : A( val ) {}
};
D variable1( "Hello" );
D variable2( 10 );
C++11 allows you to use the using A::A syntax you use in your decleration of D, but C++11 features aren't supported by all compilers just now, so best to stick with the older C++ methods until this feature is implemented in all the compilers your code will be used with.
class A
{
public:
A(int val) {}
A(string name) {}
};
class B : public A
{
using A::A;
};
Addition to other answers, in case there are several constuctors with the base class, and you just want to inherit some of them, you can delete the unwanted.
class B : public A
{
using A::A;
B(string) = delete;
};
Here is a good discussion about superclass constructor calling rules. You always want the base class constructor to be called before the derived class constructor in order to form an object properly. Which is why this form is used
B( int v) : A( v )
{
}
Is it safe to call non-virtual base methods from member initializer list? And virtual?
It is not safe to call any member function (virtual or not virtual) before all base have been initialized. Bellow an example given in the standard ([class.base.init]§16):
class A {
public:
A(int);
};
class B : public A {
int j;
public:
int f();
B() : A(f()), // undefined behavior: calls member function but base A not yet initialized
j(f()) { } // well-defined: bases are all initialized
};
class C {
public:
C(int);
};
class D : public B, C {
int i;
public:
D() : C(f()), // undefined behavior: calls member function but base C not yet initialized
i(f()) { } // well-defined: bases are all initialized
};
There are more subtle cases.
As I was saying in the comment:
The first thing that is initialized in the initializer list of a derived class is the base class. Explicitly it looks like this:
class A{ ... };
class B : public A {
int x, y;
B() : A{}, x{...}, y{...} {
...
}
};
Therefore, when initiallizing x and y you can call any non virtual method of A, as it is already constructed.
The second part of the question doesn't have much to do with virtualness - It is simply a question of whether you can call a member function in the constructor. The answer is yes, but - you need to make sure you don't use any uninitialized parts of the object.
e.g.
struct Base {
virtual int f(int i) = 0;
};
struct Derived : public Base {
int x;
int y;
virtual int f(int i) override { return i; }
Derived(int i) : Base{}, x{f(i)}, y{f(x)} {}
};
is fine, but writing ... Derived(int i) : Base{}, x{f(y)}, y{f(i)} ... is not.
The code below gives a compile-time error but when I remove the keyword "virtual" from below it works fine. I know that virtual inheritance is done to avoid multiple copies of base class members, but what is the significance of the keyword "virtual" here??
#include<iostream>
using namespace std;
class A {
int a;
A() {
cout<<"I am A\n";
a = 1;
}
public :
friend class B;
};
class B : virtual public A {
int b;
public :
B() {
cout<<"I am B\n";
b = 2;
}
};
class C : public B {
int c;
public :
C() {
cout<<"I am C\n";
c = 3;
}
};
int main()
{
C var;
return 0;
}
Initializaion of virtual base classes follows different logic than regular base classes.
From https://en.cppreference.com/w/cpp/language/derived_class#Virtual_base_classes:
All virtual base subobjects are initialized before any non-virtual base subobject, so only the most derived class calls the constructors of the virtual bases in its member initializer list:
In your case, the constructor of C can be thought of as:
C() : A(), B() {
cout<<"I am C\n";
c = 3;
}
That is a problem since A() is declared private.
When A is a regular base class of B, C() can be thought of as:
C() : B() { // No A()
cout<<"I am C\n";
c = 3;
}
That is not a problem given your posted code.
The compiler is complaining the constructor of D is deleted because of ill forming why ?
#include<iostream>
using namespace std;
class A
{
int x;
public:
A(int i) { x = i; }
void print() { cout << x; }
};
class B: virtual public A
{
public:
B():A(10) { }
};
class C: virtual public A
{
public:
C():A(10) { }
};
class D: public B, public C {
};
int main()
{
D d;
d.print();
return 0;
}
Output
main.cpp:37:4: error: use of deleted function 'D::D()' D d;
^ main.cpp:32:7: note: 'D::D()' is implicitly deleted because the default definition would be ill-formed: class D: public B, public C {
^
Due to the rules for initialization of virtual base classes,
class D: public B, public C {
};
is equivalent to:
class D: public B, public C {
public:
D() : A(), B(), C() {}
};
That's why you cannot create in instance of D.
Solution 1
Change A so it has a default constructor.
class A
{
int x;
public:
A(int i = 0) { x = i; }
void print() { cout << x; }
};
Solution 2
Change D to:
class D: public B, public C {
public:
D() : A(0), B(), C() {}
};
or a simpler version,
class D: public B, public C {
public:
D() : A(0) {}
};
That's because D inherits from A indirectly using virtual. A doesn't have a parameterless constructor so a compiler-generated constructor for D can't be made.
Note: this is mostly just adding a reference to the standard, in case anybody might care (but as usual for him, #R. Sahu's answer is quite accurate).
The standard specifies ([class.base.init]/13) that:
In a non-delegating constructor, initialization proceeds in the
following order:(13.1) — First, and only for the constructor of the
most derived class (6.6.2), virtual base classes are 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 classes in the derived class
base-specifier-list.(13.2) — Then, direct base classes are
initialized in declaration order as they appear in the
base-specifier-list (regardless of the order of the mem-initializers).
So, since A is a virtual base class, it's initialized directly by the most derived class (D). Only afterward, the direct base classes are initialized--but for anything to compile, the most derived class must be able to initialize the virtual base class(es).
There is one point some might find interesting in a case like this. Let's modify your class structure just a tiny bit, so we to the necessary initialization, and (importantly) initialize with a unique value in each constructor:
#include <iostream>
class A {
int i;
public:
A(int i) : i(i) {}
void show() { std::cout << "value: " << i << "\n"; }
};
class B : virtual public A{
public:
B() : A(10) {}
};
class C : virtual public A {
public:
C() : A(20) {}
};
class D : public B, public C {
public:
D() : A(0) {}
};
int main() {
D d;
d.show();
}
In this case, what exactly happens? We have three different constructors each "thinking" it's going to initialize the A object with a different value? Which one "wins"?
The answer is that the one in the most-derived constructor (D::D) is the one that' used to initialize the virtual base class object, so that's the one that "wins". When we run the code above, it should print 0.
I have the following structs
struct A
{
int i;
A() { i = 0; }
A(int _i) : i(_i) {}
virtual void f() { cout << i; }
};
struct B1 : virtual A {
B1() : A(1) { f(); }
void f() { cout << i+10; }
};
struct B2 : virtual A {
B2(int i) : A(2) { f(); }
};
struct C : B1, B2 {
C() : B2(3) {}
};
please explain why the following code prints 100: (10+0)
C* c = new C();
I think it should print 1111:
first A() : i = 0
then B1() : i = 1 and prints B1::f() - 11
then B2() : prints prints B1::f() - 11
Where I go wrong?
Thanks
Due to the inheritance, the C object "contains" a B1 object and a B2 object. Both the B1 and the B2 object "contain" an A object, but since you have virtual inheritance of A, you only have one A object in each C object, not two. This single A object is initialized using A's default constructor.
Public by default in a struct.
It doesn't print 100, it prints 10 followed by 0.
Struct means that the fields are private by default or public?
Public.