QObject 作为另一个 QObject 的字段?

Posted

技术标签:

【中文标题】QObject 作为另一个 QObject 的字段?【英文标题】:QObject as a field of another QObject? 【发布时间】:2015-10-22 08:50:12 【问题描述】:

我们通常编写拥有其他 QObject 的 QObject 派生类,如下所示:

class Foo: public QObject 
    Q_OBJECT
public:
    Foo(QObject *parent = nullptr) 
    : QObject(parent), bar(nullptr)
    
        bar = new Bar(this);
    
private:
    Bar *bar;
;

但是,在查看同事的代码时,我发现了这种模式:

class Foo: public QObject 
    Q_OBJECT
public:
    Foo(QObject *parent = nullptr) 
    : QObject(parent)
     
private:
    Bar bar;
;

它似乎按预期工作,但这样做实际上是否安全,除非有明显的“不要bar->deleteLater()”?

【问题讨论】:

【参考方案1】:

是的,这样做是安全的,只要您不尝试删除 bar,因为它会在删除 Foo 时自动释放。

可以说,走哪条路的决定取决于 FooBar 的实际情况。

Bar 是一个类的组合时,我更倾向于使用第二种方法。在这种情况下,它是它的一部分,这意味着没有 Bar,Foo 没有意义。

相比之下,如果 Bar 是一个聚合类,则指向其对象的指针更实用。在这种情况下,Bar 为 Null,Foo 仍然存在。

例如,Foo 是 Car,Bar 是司机。一辆车可能有也可能没有司机,但它仍然是一辆车,所以车上的指针会更有意义。此外,这允许通过简单地更改指向它的指针来更改汽车中的驾驶员。

【讨论】:

说,Foo实现了一些需要咨询网络的功能,而Bar就是QTcpSocket。是聚合还是关联? 如果对象 Foo 在没有 Bar 的情况下仍然可以被分类并作为对象起作用,那么它就是聚合。如果 Foo 需要 Bar 作为对象才有意义,那么它就是组合。不要太担心条款。相反,请考虑对象的生命周期。如果 Bar 是 QTcpSocket,Foo 应该在没有 Bar 的情况下存在还是 Foo 可以使用不同的 Bar?如果是,则使用指针。【参考方案2】:

更好的问题是:为什么通过将bar 作为指针的额外间接来过早地悲观您的代码?第二种方法不仅安全(为什么不安全?),由于引用的局部性、堆碎片等原因,它是首选的。第二个代码片段中唯一缺少的是 bar 的父级的初始化。如果您希望将 Foo 实例移动到另一个线程,则需要这样做。如果不这样做,您将过早地任意限制Foo 的功能。

在 C++11 中:

class Bar : public QObject  ... ;

class Foo: public QObject 
    Q_OBJECT
public:
    Foo(QObject *parent = nullptr) : QObject(parent) 
private:
    Bar bar  this ;
;

【讨论】:

这不会导致bar 的双重破坏吗?一次在~QObject() 中,又一次在编译器生成的“调用成员的析构函数”中 `~Foo()"? @Joker_vD 不,不会。我不明白这种普遍的误解是从哪里来的。 QObject 对它的孩子没有无限的记忆。如果你摧毁了一个对象的任何孩子,它会立即忘记它们;任何其他行为都是无稽之谈。因此,一旦bar.~Bar 运行,您就清楚了。 Foo 基地~QObject 会做正确的事。【参考方案3】:

已保存且 100% 有效。然而,第二段代码只有在Bar 有一个不带参数的构造函数时才有效。

但是,如果Bar 的构造函数需要参数,您仍然需要将其创建为Foo 构造函数的初始化列表的一部分。

class Foo: public QObject 
    Q_OBJECT
public:
    Foo(QObject *parent = nullptr) 
    : QObject(parent)
    , bar(arg1, arg2)
     
private:
    Bar bar;
;

【讨论】:

【参考方案4】:

这应该是安全的。正如您所指出的,您当然不应该删除 bar,因为它将被 Foo 的析构函数删除。如果在 bar 上设置了父级,则应小心,以免发生双重删除。

【讨论】:

正如 Kuba Ober 所说,您的最后一句话是不正确的。在(非指针)对象成员变量上设置父级很好(它不是绝对必要的,但对于在父级上调用 MoveToThread 后保持适当的线程亲和性等事情很有帮助)。一旦被删除,QObject 析构函数就不会再尝试删除它。 @ScottG 没关系,成员变量 QObject 的父级是封闭的 QObject。这不是无条件的好。如果 parent 是别的东西,并且首先被破坏,它会崩溃和燃烧。

以上是关于QObject 作为另一个 QObject 的字段?的主要内容,如果未能解决你的问题,请参考以下文章

访问从另一个 QObject 类继承的 QObject 类

QObject 真的有多重? [复制]

从 QList<QObject*> 填充 ComboBox

10.4 事件过滤器

强制 QObject 作为 QWidget 的父级有啥后果?

分配两个 QObject [关闭]