假设我有一个类,它通过将自身传递给多个成员指针来初始化它们(减少到仅
A
)。传递父窗口小部件是Qt中的常见做法。
class B : public Base{
public:
B() :
my_a(new A(this))
{ ... }
A* my_a;
};
但是为了使其可测试,我想从外部设置这些指针,最好传入构造函数而不重复括号之间的所有内容。
可能看起来像这样:
class B : public Base{
public:
B() :
B(new A(this))
{ }
B(A* a) :
my_a(a)
{ ... }
A* my_a;
};
从
B
的默认构造函数中,我们将其称为专用版本。但问题是它会导致未定义的行为 - 有时会崩溃,但有时会产生“不可能”的输出。我能够将范围缩小到 : B(new A(this))
部分 - 在使用 this
创建新对象时调用委托构造函数。
问题:在这种情况下是什么导致了 UB?在使用当前实例初始化新对象时是否可以使用委托构造函数?
如果没有任何效果,我将创建 2 个构造函数而不进行委托,并创建一个
init
方法来删除重复代码。
诗。将
Base
视为 QWidget
,而 A
和 B
是自定义小部件。
编辑: 这是一个可以编译的简短版本,但不同的运行可以产生不同的输出:
#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;
}
在
B() : B(new A(this))
中,必须创建 A
的实例作为评估委托构造函数的参数的一部分。这意味着它发生在 this
的任何初始化之前,包括调用基类类构造函数之前。在这种情况下,当构造 my_i
时,A
尚未初始化。然后,您可以在调用 get_int();
时使用未定义的行为来读取未初始化的数据成员。
在
B() : my_a(new A(this))
中,A
的实例是在成员初始化期间创建的,这总是在基类构造函数完成之后。在这种情况下,my_i
在构造A
之前初始化。