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.
Related
Since I maked x as protected, shouldn't class B inherit x from A?
class A {
public:
A() {
}
protected:
int x = 0;
};
class B: public A{
B():x(1){
}
};
int main()
{
B b;
}
I'm getting that x does not exist on B
Inherited member variables cannot be set in the initializer list of the constructor. You can either initialize it after your brackets, or do something like this:
class A {
public:
A(int x) : x(x) {
}
protected:
int x = 0;
};
class B : public A {
public:
B() : A(1) {
}
};
int main()
{
B b;
return 0;
}
Since I maked x as protected, shouldn't class B inherit x from A?
Protected doesn't mean that the member is "inherited". A base is inherited, and the base contains all of its members (including the private ones). Protected access specifier means that the derived class has access to the name.
A base can only be initialised with a constructor of base. Members of a base cannot be initialised separately from it. Following would be correct:
struct B {
B(int x) : x(x) {}
protected:
int x = 0;
};
struct D : B{
D() : B(1) {}
};
Since I maked x as protected, shouldn't class B inherit x from A?
Yes, it does. You can use it inside B constructor or any B member functions, eg
B() { x = 2; } // ok
I'm getting that x does not exist on B
However, you can't initialise x in the initialisation list. So thing like this won't work:
B():x{1} // no
You can only initialise x it A constructor where it is a member variable.
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 a class A and two children B and C as follows:
class A {
private:
int x;
template<class T>
void setX(T &y);
public:
A();
};
class B : public A {
private:
static const double y;
public:
B();
};
class C : public A {
private:
static const int y;
public:
C();
};
Both children only differ in the type of their static member y. The implementation of both C and B is the same except on the initialization of the static member:
B::B() : y (1.2) { setX(y) }
C::C() : y (2) { setX(y) }
But the problem with this approach is that in the implementation file I have to write twice the same code for B and C. Is there a proper way to write this such that I do not need to write twice the call to setX?
In the real problem the classes are a little more complicated, but the situation at hand is the same. In particular, initialization of y requires non-trivial constructors and so it has to be in the implementation file.
You can write a constructor for A as a function template.
class A {
//....
public:
template<typename T>
explicit A(T& y) {
setX(y);
}
};
And call that constructor from child classes:
class B : public A{
//...
public:
B() : A(1.2), y(1.2)
{}
};
Only problem is that base class constructor gets called first, so you need to repeat constant data value twice. You can easily macro it though.
class Base
{
public:
Base(){}
Base(int k):a(k)
{
}
int a;
};
class X:virtual public Base
{
public:
X():Base(10){}
int x;
};
class Y:virtual public Base
{
public:
Y():Base(10){}
int y;
};
class Z:public X,public Y
{
public:
Z():X(10){}
};
int main()
{
Z a;
cout << a.a;
return 1;
}
In the above case, for Z():X(10){} Base(int k):a(k) is not calling, but when i change to Z():Base(10){} the Base(int k):a(k) is called. Why ?
Thank you.
Because you used the virtual keyword - that's exactly what it does.
You have to explicitly initialize Base in the initializer list of Z in order to disambiguate between the initialization in X and the initalization in Y.
See this question. The gist is, that when using virtual inheritance you have to call the base class constructor explicitly.
The initializer list in the most derived constructor is used to initialize your base classes. Since class Z inherits from class X and Y which inherits from a common base class, the virtual keyword is used to create only a single subobject for the base class in order to disambiguate when accessing the data member a.
Why I can't access base class A's a member in class B initialization list?
class A
{
public:
explicit A(int a1):a(a1)
{
}
explicit A()
{
}
public:
int a;
public:
virtual int GetA()
{
return a;
}
};
class B : public A
{
public:
explicit B(int a1):a(a1) // wrong!, I have to write a = a1 in {}. or use A(a1)
{
}
int GetA()
{
return a+1;
}
};
class C : public A
{
public:
explicit C(int a1):a(a1)
{
}
int GetA()
{
return a-1;
}
};
A's constructor runs before B's, and, implicitly or explicitly, the former construct all of A's instance, including the a member. Therefore B cannot use a constructor on a, because that field is already constructed. The notation you're trying to use indicates exactly to use a constructor on a, and at that point it's just impossible.
To build on Alex' answer, you can initialize the base class' "a" member by controlling its construction, like so:
class B : public A
{
public:
explicit B(int a1) : A(a1) { } // This initializes your inherited "a"
...
};
Note that I'm constructing the base class (capital "A") above, rather than attempting to directly initialize its inherited member (lowercase "a", drawing from your example).
To build even further on pilcrow's answer, you could easily initialize the A member like you want by overriding it in your B class:
class B : public A
{
public:
int a; // override a
explicit B(int a1) : a(a1) // works now
{
}
...
};
Though, I wouldn't necessarily recommend this ;)