如何区分"QObject"的亲子关系关注点,例如与其他所有者进行互操作?



考虑此用例:当QObject的寿命在其他地方进行管理时,例如通过C 范围的寿命(作为本地变量,或类成员等),或使用共享指针,其父母不应尝试将其删除在~QObject() Destructor中。有没有办法将对象的所有权真正传递给共享指针,以便父母不会尝试删除它?

如果我们不打算所有权,则可能是建立父母的原因?它们存在源于以下事实:QT中的多种目的是为了将它们拨入的事实,并且没有内在的方式将它们解除:

  1. [gc] 父母充当孩子对象的垃圾收集器:如果有任何人在父母的销毁之前生存下来,父母将销毁并销售。

  2. [thread] 孩子的线程亲和力遵循父母的亲和力。

    QObject开始支持多线程操作并获得thread属性时,这添加到QT 4中。

  3. [widgettree] 小部件使用亲子关系作为小部件树的边缘。

    • 除非小部件具有Qt::Window标志 - 然后它是顶级小部件,并且是其自己的小部件树的根,但仍然是由父的垃圾收集的。

直接的目标是将[GC]与其他功能解除,并允许将其禁用。扩展的目标是将所有三个功能彼此解脱。

[gc]

我们可以拦截父母的~QObject()驱动器,并在删除该列表中的子对象之前从子列表中删除对象。幸运的是,~QObject在删除孩子之前发出destroyed信号。

因此,要将对象的父母更改为非拥有父母,我们只有在重要的情况下才能采取行动:当父母的销售器被调用时。因此,我们

  1. 拦截父母的destroyed信号并清除对象的父。

  2. 拦截父母失去孩子,或者,如果父母未收到此类事件,请拦截发送给孩子的事件并将其用作钝钩检测父母是否更改了。

这可以在独立的实用程序功能和助手类中实现,而无需修改所涉及的任何对象的代码。

// https://github.com/KubaO/stackoverflown/tree/master/questions/qobject-sep-concerns-55046944
#include <QtWidgets>
class ParentTracker : public QObject {
   QMetaObject::Connection connection;
   QObject *subjectParent = nullptr;
   inline QObject *subject() const { return parent(); }
   bool eventFilter(QObject *receiver, QEvent *event) override {
      qDebug() << receiver << event->type();
      if (receiver == subject()) {
         // Track parent changes on the child
         if (subject()->parent() != subjectParent) {
            detachFromParent();
            attachToParent();
         }
      } else if (event->type() == QEvent::ChildRemoved) {
         // Track child changes on the parent
         Q_ASSERT(receiver == subjectParent);
         auto *ev = static_cast<QChildEvent *>(event);
         if (ev->child() == subject()) {
            detachFromParent();
         }
      }
      return false;
   }
   void lostParent() {
      subject()->setParent(nullptr);
      detachFromParent();
   }
   void detachFromParent() {
      if (subjectParent) {
         disconnect(connection);
         connection = {};  // free the connection handle immediately
         subjectParent->removeEventFilter(this);
         subjectParent = nullptr;
      }
   }
   void attachToParent() {
      Q_ASSERT(!subjectParent);
      subjectParent = subject()->parent();
      bool snoopChild = !subjectParent;
      {
         auto *widget = qobject_cast<QWidget *>(subject());
         snoopChild = snoopChild ||
                      (widget && widget->testAttribute(Qt::WA_NoChildEventsForParent));
      }
      if (subjectParent) {
         auto *widget = qobject_cast<QWidget *>(subjectParent);
         snoopChild = snoopChild ||
                      (widget && widget->testAttribute(Qt::WA_NoChildEventsFromChildren));
         connection = connect(subjectParent, &QObject::destroyed, this,
                              &ParentTracker::lostParent);
      }
      if (snoopChild)
         subject()->installEventFilter(this);
      else {
         Q_ASSERT(subjectParent);
         subject()->removeEventFilter(this);
         subjectParent->installEventFilter(this);
      }
   }
  public:
   explicit ParentTracker(QObject *child) : QObject(child) {
      Q_ASSERT(subject());
      attachToParent();
   }
};
ParentTracker *detachQObjectOwnership(QObject *child) {
   Q_ASSERT(child && (!child->thread() || child->thread() == QThread::currentThread()));
   QObject *parent = child->parent();
   if (!parent) return nullptr;
   if (parent->thread() != child->thread()) return nullptr;
   return new ParentTracker(child);
}
template <class T> void setup(QPointer<QObject> &parent, QPointer<QObject> &child) {
   parent = new T;
   child = new T(static_cast<T*>(parent.data()));
   parent->setObjectName("parent");
   child->setObjectName("child");
   Q_ASSERT(parent && child);
}
int main(int argc, char *argv[]) {
   QApplication app(argc, argv);
   QPointer<QObject> parent, child, tracker;
   // parent-child ownership
   setup<QObject>(parent, child);
   delete parent;
   Q_ASSERT(!parent && !child);
   // parent-child without ownership
   setup<QObject>(parent, child);
   tracker = detachQObjectOwnership(child);
   delete parent;
   Q_ASSERT(!parent && child && tracker);
   delete child;
   Q_ASSERT(!parent && !child && !tracker);
}

不,你不能。

您的要求等于两个std::unique_ptr指向同一资源。

一个是您的C 自定义所有者,另一个是QObject父母。

虽然有一个中间解决方案,但对于您的一半问题:使用QPointer。该类持有QObject指针,但如果指向QObject被销毁,则将其设置为空。拥有QPointer的自定义WeakQObjectPtr类型RAII类可能会做您想要的。尽管无法处理,但它无法处理班级成员或本地变量。

您在QT世界中,按照QT规则生活,或者不使用它们。后者并不总是可能。

相关内容

  • 没有找到相关文章

最新更新