考虑此用例:当QObject
的寿命在其他地方进行管理时,例如通过C 范围的寿命(作为本地变量,或类成员等),或使用共享指针,其父母不应尝试将其删除在~QObject()
Destructor中。有没有办法将对象的所有权真正传递给共享指针,以便父母不会尝试删除它?
如果我们不打算所有权,则可能是建立父母的原因?它们存在源于以下事实:QT中的多种目的是为了将它们拨入的事实,并且没有内在的方式将它们解除:
-
[gc] 父母充当孩子对象的垃圾收集器:如果有任何人在父母的销毁之前生存下来,父母将销毁并销售。
-
[thread] 孩子的线程亲和力遵循父母的亲和力。
当
QObject
开始支持多线程操作并获得thread
属性时,这添加到QT 4中。 -
[widgettree] 小部件使用亲子关系作为小部件树的边缘。
- 除非小部件具有
Qt::Window
标志 - 然后它是顶级小部件,并且是其自己的小部件树的根,但仍然是由父的垃圾收集的。
- 除非小部件具有
直接的目标是将[GC]与其他功能解除,并允许将其禁用。扩展的目标是将所有三个功能彼此解脱。
[gc]
我们可以拦截父母的~QObject()
驱动器,并在删除该列表中的子对象之前从子列表中删除对象。幸运的是,~QObject
在删除孩子之前发出destroyed
信号。
因此,要将对象的父母更改为非拥有父母,我们只有在重要的情况下才能采取行动:当父母的销售器被调用时。因此,我们
-
拦截父母的
destroyed
信号并清除对象的父。 -
拦截父母失去孩子,或者,如果父母未收到此类事件,请拦截发送给孩子的事件并将其用作钝钩检测父母是否更改了。
这可以在独立的实用程序功能和助手类中实现,而无需修改所涉及的任何对象的代码。
// 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规则生活,或者不使用它们。后者并不总是可能。