良好编码实践的一个重要经验法则是
每个
必须与new
匹配delete
在上一个问题this中讨论过。通常,我们将此规则封装在 RAII 类中,如
std::unique_ptr
。然而,Qt 似乎打破了这种模式。例如,要创建 QTimer
,用户可以执行以下操作:
QTimer *timer = new QTimer(this);
connect(timer, &QTimer::timeout, this, QOverload<>::of(&AnalogClock::update));
timer->start(1000);
上面的例子可以在这里找到。
QTimer
构造函数传递 this
指针,该指针成为 timer
的父级。当 *this
的析构函数被调用时,Qt 似乎会设置 QTimer
来析构 *this
。实际上,无需调用 delete timer
。
delete timer;
,例如在 unique_ptr
的析构函数中?这会调用析构函数两次吗?timer
分配在堆栈上怎么办? *this
知道吗?我可以调用删除计时器吗
如果您显式调用
delete timer
,QTimer 的析构函数(准确地说,QTimer 的 QObject 基类的析构函数)将从其父对象的子对象列表中删除 QTimer,以便稍后当父对象时调用对象自己的析构函数时,QTimer 不会被第二次删除。所以没关系。
例如在 unique_ptr 的析构函数中
使用
unique_ptr
(或shared_ptr
),OTOH,你会遇到一个问题——虽然 QObject 类足够聪明,可以在销毁时更新其父 QObject,但它们不知道如何更新unique_ptr
或 shared_ptr
指向它们。因此,如果您分配一个 unique_ptr
或 shared_ptr
来指向您的 QTimer 对象,并且还为 QTimer 对象提供一个非 NULL 父对象(如您的示例中所示),那么您可能 will 会遇到双删除问题:一次是当 Qt 机制删除 QTimer 时,第二次是当 unique_ptr
尝试删除它包含的(现在悬空的)指针时。
正因为如此,最好不要尝试混合 Qt 的对象树系统和 C++ 智能指针。也就是说,如果您要为 QTimer 提供一个非 NULL 父级,那么您应该依赖该父级来处理 QTimer 的删除,而不是尝试使用
unique_ptr
来跟踪它。
如果定时器分配在堆栈上怎么办? *这知道吗?
不,
this
的析构函数不知道该对象在堆栈上,因此它会尝试删除它,从而导致未定义的行为。因此,如果您要在堆栈上分配 QTimer,则不应为其指定非 NULL 父指针。