找到`destroyed(QObject *)`信号的发送者

时间:2015-12-28 18:57:05

标签: c++ qt signals-slots

我目前想知道如何合理使用QObject::destroyed(QObject*) signal

观察

我注意到QWidget - 派生对象的处理方式略有不同。请考虑以下小型自包含和编译示例:

/* sscce.pro:
QT += core gui widgets
CONFIG += c++11
TARGET = sscce
TEMPLATE = app
SOURCES += main.cpp
*/

#include <QApplication>
#include <QPushButton>
#include <QTimer>
#include <QtDebug>

int main(int argc, char *argv[]) {
    QApplication app(argc, argv);

    QPushButton *button = new QPushButton;
    QObject::connect(button, &QPushButton::destroyed,
        [=](QObject *o) { qDebug() << o; });

    delete button;

    QTimer *timer = new QTimer;
    QObject::connect(timer, &QTimer::destroyed,
        [=](QObject *o) { qDebug() << o; });

    delete timer;

    return app.exec();
}

这是它的输出:

QWidget(0x1e9e1e0)
QObject(0x1e5c530)

所以假设信号是从QObject的d-tor发出的,所以当为QObject调用插槽时,只剩下QTimer个基数。但是,QWidget的d-tor似乎是拦截的,因为它仍然从插槽中将自己标识为QWidget

问题

假设我们有一个计时器池,可以在QList<QTimer *>中组织几个计时器:

struct Pool {
    QTimer *getTimer() {
        return timers.at(/* some clever logic here */);
    }        

    QList<QTimer *> timers;
};

现在一个不谨慎的用户可能会删除借给他/她的计时器。好吧,我们可以做出反应并简单地从列表中删除该计时器。一个插槽可以解决这个问题:

Pool::Pool() {
    /* for each timer created */
    connect(theTimer, SIGNAL(destroyed(QObject*),
        this, SLOT(timerDestroyed(QObject*));
}

void Pool::timerDeleted(QObject *object) {
    QTimer *theTimer = /* hrm. */
    timers.removeOne(theTimer);
}

但现在呢?人力资源管理。调用插槽时,QTimer已经处于破坏状态并被部分破坏 - 只剩下其QObject个碱基。所以我不能qobject_cast<QTimer *>(object)

要解决此问题,我可以考虑以下技巧:

  1. QObject存储在列表中。然后,每次我使用列表中的项目时,我都必须向下转发。不过,我可以使用static_cast完成此操作,因为我知道列表中只有QTimer个,因此不需要dynamic_castqobject_cast
  2. removeOne的Insteat使用iterator遍历列表,然后将每个QTimer项直接与QObject进行比较。然后使用QList::erase等。
  3. static_cast甚至是reinterpret_cast QObjectQtimer
  4. 我该怎么办?

    谢谢,圣诞快乐,新年快乐: - )[*]

    [*]:这个问题完成后会清理它。

4 个答案:

答案 0 :(得分:1)

如果你正在寻找技巧,你可以简单地使用基础QObject objectName并根据它删除被破坏的计时器。

答案 1 :(得分:1)

很明显,你的问题是对象所有权问题;特别是,如何传达谁负责摧毁一个物体。如果您的Pool对象拥有 QTimer对象(因此用户不应delete他们),请通过界面清楚说明,例如返回QTimer&而不是{来自getTimer方法的{1}}。我不是很精通Qt,但如果你真的想传输从方法返回的对象的所有权,从而让用户负责删除它,你可能会返回{{ 1}}。

答案 2 :(得分:1)

直接演员:

void Pool::timerDeleted(QObject *object) {
    QTimer *theTimer = (QTimer*)object; //qobject_cast doesn't work here
//we are sure that only a timer can be a sender
    timers.removeOne(theTimer);
}

答案 3 :(得分:0)

您可以将列表基于QPointer而不是原始指针。即写

QList<QPointer<QTimer>> timers;

现在当列表中的一个定时器消失时,列表中的相应条目将自动清除。但是不会被删除!但是当您通过getTimer()方法访问计时器时,已删除计时器的条目现在将返回nullptr(而不是悬空指针)。

是的,QWidget在自己的析构函数中发出destroyed()。这就是为什么在这种情况下你会看到一个真正的QWidget。其他人都使用QObject的实施。