Qt Destructor不会断开信号/插槽

问题描述 投票:-1回答:1

我发现Qt的这种奇怪的行为:我希望如果对象被破坏,它会自动断开连接到其插槽的所有“传入”信号。但是,下面的小例子演示了如果信号是从类成员的析构函数发出的(在主类的析构函数之后自动调用),它仍然被主类的插槽接收。

我是否有一个普通的行为,我有一个实际上不存在的对象的插槽调用? example.h文件的源代码:

#include <QObject>
#include <iostream>

class Part: public QObject {
    Q_OBJECT
public:
    Part(QObject *parent = nullptr) : QObject(parent) {
       std::cout << "Part::Part()" << std::endl;
    }
    ~Part() {
       std::cout << "Part::~Part()" << std::endl;
       emit someSignal();
   }
signals:
    void someSignal();
};


class Foo: public QObject {
    Q_OBJECT
public:
    Foo(QObject *parent = nullptr) : QObject(parent) {
        m_part = new Part(this);
        std::cout << "Foo::Foo()" << std::endl;
        connect(m_part, &Part::someSignal, this, &Foo::slotFunc);
    }
    ~Foo() {
        std::cout << "Foo::~Foo()" << std::endl;
    }

public slots:
    void slotFunc() {
        std::cout << "Foo::slotFunc()" << std::endl;
    }
private:
    Part *m_part;
};

main.cpp文件只包含:

{
    Foo obj;
}

输出是:

Part::Part()
Foo::Foo()
Foo::~Foo()
Part::~Part()
Foo::slotFunc()
qt destructor signals-slots
1个回答
0
投票

它显然不会发生 - 至少不是我理解你所做的事情,而不是你的代码是按照面值进行的。涉及给定对象的所有连接在该对象的子节点被删除之前断开,这是QObjectQWidget经历破坏的最终行为之一。在版本4.8之前的Qt就是这种情况,我已经在4.8,5.9,5.10和5.11上进行了检查。行为是一样的:你描述的是不可能的。

如果使用Foo::slot除了foo以外的任何类型调用槽,下面的测试将无法在Foo内部进行断言。我恳请您下载代码,自己构建并运行它。断言在调试和释放模式下都会保留(由于QT_FORCE_ASSERTS),因此释放模式不会使测试无效。代码应该不需要为你编译而改变 - 如果是的话,它表明了一些必须首先解决的其他问题,并且理解但不是以不屑一顾的方式。

Scope类只是一个帮助程序,用作程序计数器的代理,并且可以断言代码正在执行的操作。

输出如下所示(测试的3个场景中的第一个):

*** before the application object is created
part_constructor_body *ran*
part_constructor_body *ran*
part_constructor_body *ran*
part_constructor_body *ran*
part_constructor_body *ran*
part_constructor_body *ran*
part_constructor_body *ran*
Foo_Type (enter)
foo_constructor_body (enter)
foo_constructor_body (leave)
foo_destructor_body *ran*
part_destructor_body (enter)
foo_slot_body *ran*
part_destructor_body (leave)
part_destructor_body (enter)
foo_slot_body *ran*
part_destructor_body (leave)
part_destructor_body (enter)
foo_slot_body *ran*
part_destructor_body (leave)
part_destructor_body (enter)
foo_slot_body *ran*
part_destructor_body (leave)
part_destructor_body (enter)
foo_slot_body *ran*
part_destructor_body (leave)
part_destructor_body (enter)
foo_slot_body *ran*
part_destructor_body (leave)
Foo_Type (leave)
part_destructor_body "part4" (enter)
****
part_destructor_body "part4" (leave)

在标有foo_slot_body的地方有一个****,以及由于断言失败导致的崩溃。这不会发生。

// https://github.com/KubaO/stackoverflown/tree/master/questions/object-lifetime-conundrum-52221660
#define QT_FORCE_ASSERTS
#include <QtCore>
#include <memory>

static const char kEnter[8] = "(enter)", kLeave[8] = "(leave)", kBlip[6] = "*ran*";
class Scope {
   Q_DISABLE_COPY(Scope)
   QObject *const obj;
   QByteArray const property;
   static QObject &proprietor() {
      static QObject p;
      return p;
   }
   static void indicate(const char *prop, QObject *obj, const char *event) {
      auto dbg = QDebug(QtMsgType::QtDebugMsg) << prop;
      if (!obj->objectName().isEmpty()) dbg << obj->objectName();
      dbg << event;
      Q_ASSERT(isIn(prop) == (event == kLeave));
      proprietor().setProperty(prop, event == kEnter);
   }

  public:
   enum When { Out = 1, InOut = 2 } const when;
   Scope(const char *p, QObject *o, When w = InOut) : obj(o), property(p), when(w) {
      if (when == InOut) in();
   }
   void in() { indicate(property, obj, kEnter); }
   ~Scope() { indicate(property, obj, kLeave); }
   struct GoIn {
      GoIn(Scope &scope) { scope.in(); }
   };
   static void blip(const char *prop, QObject *o) { Scope::indicate(prop, o, kBlip); }
   static void shred(const char *prop) { proprietor().setProperty(prop, {}); }
   static bool had(const char *prop) { return proprietor().property(prop).isValid(); }
   static bool isIn(const char *prop) { return proprietor().property(prop).toBool(); }
};

class Part : public QObject {
   Q_OBJECT
  public:
   Part(QObject *parent = nullptr) : QObject(parent) {
      Scope::blip("part_constructor_body", this);
      Q_ASSERT(!Scope::isIn("Foo_Type"));
   }
   ~Part() override {
      Scope scope("part_destructor_body", this);
      emit signal();
   }
   Q_SIGNAL void signal();
};

class Foo : public QObject {
   Q_OBJECT
   Scope scope{"Foo_Type", this, Scope::Out};
   Part part1{this};  // a child owned by value - bravo! - the lowest overhead approach
   Part part2;        // ditto, made a child in the constructor's initializer list
   Part part3;        // fine, but not a child of Foo, and thus Foo's `moveToThread()`
                      // will almost always set Part up for undefined behavior
   // the below all have the overhead of an extra indirection - an entirely gratuitous one
   std::unique_ptr<Part> part1b{new Part(this)};
   std::unique_ptr<Part> part2b;
   std::unique_ptr<Part> part3b{new Part};
   // and the snafu
   Part *part4{new Part(this)};
   Scope::GoIn into{scope};

  public:
   Foo(Qt::ConnectionType type = Qt::AutoConnection)
       : part2(this), part2b(new Part(this)) {
      Scope scope("foo_constructor_body", this, Scope::InOut);
      part4->setObjectName("part4");
      for (auto *p :
           {&part1, &part2, &part3, part1b.get(), part2b.get(), part3b.get(), part4})
         connect(p, SIGNAL(signal()), this, SLOT(slot()), type);
   }
   ~Foo() override { Scope::blip("foo_destructor_body", this); }

   Q_SLOT void slot() {
      Scope::blip("foo_slot_body", sender());
      Q_ASSERT(qobject_cast<Foo *>(this));
      Q_ASSERT(Scope::isIn("Foo_Type"));  // equivalent to the foregoing assert
   }
};

int main(int argc, char *argv[]) {
   qDebug() << "*** before the application object is created";
   Foo{};
   QCoreApplication app(argc, argv);
   qDebug() << "*** after the application object is created";
   Foo{};
   qDebug() << "*** with queued connections" << Qt::QueuedConnection;
   {
      Q_ASSERT(Scope::had("foo_slot_body"));
      Scope::shred("foo_slot_body");
      Foo foo3(Qt::QueuedConnection);  // check with queued connections
      QTimer::singleShot(1, &app, SLOT(quit()));
      app.exec();
      Q_ASSERT(!Scope::had("foo_slot_body"));
   }
}
#include "main.moc"
© www.soinside.com 2019 - 2024. All rights reserved.