This question already has answers here:
warning: derived class's member variable is initialized after base class
(3 answers)
C++: Construction and initialization order guarantees
(5 answers)
Closed 9 months ago.
Beginner here, my simplified code:
A.h
class A{
public:
A(int x);
private:
int _x;
}
A.cpp
A::A(int x)
: _x(x)
{
}
B.h
class B : public A{
public:
B()
private:
int _y = 1;
}
B.cpp
B::B()
: A(1) //works
: A(_y) //doesn't work
{
}
Why does the initialization with a member of B not work, but with a bare integer it does.
I mean I can just go for the working method but I'd like to know the cause.
Thanks for any helpers!:)
Vlad has given you the reason, here's a workaround that avoids the need to duplicate your magic number:
class B : public A{
public:
B();
private:
static const int initial_y = 1;
int _y = initial_y;
};
B::B()
: A(initial_y)
{
}
Demo
A derived class's base classes are fully initialized before any of the derived class's data members are initialized.
In the case of:
class B : public A{
public:
B();
private:
int _y = 1;
}
B::B()
: A(_y)
{
}
Data members that have initial values in their declarations are implicitly handled by the constructor's member initialization list. So, in this case, the compiler treats the above code as-if you had written it like this instead:
class B : public A{
public:
B();
private:
int _y;
}
B::B()
: A(_y), _y(1)
{
}
As you can see, A(_y) is called before _y is initialized, which is undefined behavior.
Related
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.
Class A contains the protected int x. Class B extends class A. Now what class B wants to do is set the value of x as a passing argument in its own constructor. When I try to do that, I get the error:
""x" is not a non-static data member or base class of class "B"".
#include <string>
#include <iostream>
class A {
protected:
int x;
public:
A()
{
}
};
class B : public A {
public:
B(int x)
: x(x)
{
}
};
int main()
{
}
You can "set" it, but not initialize it, because it has already been initialized when the base class object gets initialized. You can "set" it like this:
B(int x)
{
this->x = x; // assignment, not initialization
}
It would make more sense for one of A's constructors to take care of the initialization of A::x:
A(int x) : x(x) {}
and then use that in B:
using A::A; // allows B b{42};
This question already has answers here:
Order of calling constructors/destructors in inheritance
(6 answers)
Closed 5 years ago.
Consider a code
struct X {
X (int n) {...}
};
struct Base {
Base(X & x) {...}
};
struct Derived : public Base {
Derived() : Base(x), x(2) {}
X x;
};
Code works and i see no issue there but i'm concerned about the look of : Base(x), x(2) statement. First pass the instance to Base and after you initialize the actual object. Is this the the only way to express the intention?
The trick is to derived from one more base class who's function is to own the X.
Note that base class declaration order matters:
struct X {
X (int n) {}
};
struct Base {
Base(X & x) {}
};
struct OwnsAnX {
OwnsAnX(int n) : x_(n) {}
X& get_x() { return x_; }
private:
X x_;
};
struct Derived
: OwnsAnX // note: order is important
, Base
{
Derived()
: OwnsAnX(2)
, Base(get_x())
{}
// x is accessed through the inherited get_x()
};
but it's error prone if you don't keep the correct order of the classes you're inheriting from
This is a valid concern of the OP. The solution is to enable the compiler warning -Wreorder.
Reversing the order of the base classes then yields:
<source>: In constructor 'Derived::Derived()':
<source>:24:23: warning: base 'OwnsAnX' will be initialized after [-Wreorder]
, Base(get_x())
^
<source>:24:23: warning: base 'Base' [-Wreorder]
<source>:22:9: warning: when initialized here [-Wreorder]
Derived()
^~~~~~~
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.
In C++ I have a reference to an object that wants to point back to its owner, but I can't set the pointer during the containing class' construction because its not done constructing. So I'm trying to do something like this:
class A {
public:
A() : b(this) {}
private:
B b;
};
class B {
public:
B(A* _a) : a(_a) {}
private:
A* a;
};
Is there a way to ensure B always gets initialized with an A* without A holding a pointer to B?
Thanks
Try this:
class A;
class B {
public:
B(A *_a) : a(_a) {};
private:
A* a;
};
class A {
public:
A() : b(this) {};
private:
B b;
};
Since B is contained completely in A, it must be declared first. It needs a pointer to A, so you have to forward-declare A before you declare B.
This code compiles under more-or-less current versions of g++.
In C++ I have a reference to an object that wants to point back to its owner, but I can't set the pointer during the containing class' construction because its not done constructing.
You can store the pointer alright.
What you can't do is to try to get to the members/methods of A through the pointer in the constructor of B, since the parent instance might not be fully initialized at the point:
#include <iostream>
class Y;
class X
{
Y* y;
public:
X(Y* y);
};
class Y
{
X x;
int n;
public:
Y(): x(this), n(42) {}
int get_n() const { return n; }
};
X::X(Y* p): y(p)
{
//Now this is illegal:
//as it is, the n member has not been initialized yet for parent
//and hence get_n will return garbage
std::cout << p->get_n() << '\n';
}
int main()
{
Y y;
}
If you were to switch around the members in Y, so n would get initialized first, the constructor of X would print 42, but that is too fragile to depend on.