Let's say I have a class that initializes several member pointers by passing itself to them (reduced to only A
). It is a common practice in Qt to pass the parent widget.
class B : public Base{
public:
B() :
my_a(new A(this))
{ ... }
A* my_a;
};
But to make it testable I'd like to set those pointers from the outside, preferably passing in the constructor without duplicating everything between the brackets.
That might look like this:
class B : public Base{
public:
B() :
B(new A(this))
{ }
B(A* a) :
my_a(a)
{ ... }
A* my_a;
};
From B
's default constructor we call the specialized version of it. But the problem is that it cause an undefined behavior - sometimes crashing but sometimes produce 'impossible' output. I was able to narrow it down till the : B(new A(this))
part - calling a delegate constructor while creating a new object with this
.
The question: What cause the UB in that case? Is it possible to use delegate constructors while initializing new object with the current instance?
If nothing works I would create 2 constructors without delegating and an init
method to remove code duplication.
Ps. consider Base
as QWidget
, while A
and B
being custom widgets.
Edit: Here's a short version of it that can be compiled but different runs can produce different output:
#include <iostream>
#include <string>
class Base
{
public:
int get_int() const {return my_int;};
int my_int = 5;
};
class A
{
public:
A(Base* base = nullptr)
{
if(base != nullptr) {
my_i = base->get_int();
} else {
my_i = 42;
}
}
int my_i;
};
class B : public Base{
public:
B() :
B(new A(this))
{
}
B(A* a) :
my_a(a)
{}
A* my_a;
};
int main ()
{
B b;
std::cout << b.my_a->my_i;
return 0;
}
In B() : B(new A(this))
the instance of A
must be created as part of evaluating the arguments for the delegate constructor. That means it occurs before any initialization happens for this
, including before the base class constructor being called. In this case my_i
is not initialized yet when the A
is constructed. You then have Undefined Behaviour from reading an uninitialied data member while calling get_int();
.
In B() : my_a(new A(this))
the instance of A
is created in member initializer list, which means it is always created after the base class constructors have finished. In this case my_i
is initialized before the A
is constructed and get_int()
works correctly in A
's constructor.