【问题标题】:QObject as a field of another QObject?QObject 作为另一个 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()”?

【问题讨论】:

    标签: qt qobject


    【解决方案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 是别的东西,并且首先被破坏,它会崩溃和燃烧。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-05-20
      • 1970-01-01
      • 2013-02-13
      • 2015-02-24
      • 2010-12-19
      • 2020-10-29
      • 2015-07-31
      相关资源
      最近更新 更多