QObject 作为另一个 QObject 的字段?

QObject as a field of another QObject?

我们通常像这样编写拥有其他 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;
};

它似乎按预期工作,但这样做真的安全吗,除非有明显的 "don't bar->deleteLater()"?

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

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

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

相比之下,如果 Bar 是聚合的 class,则指向其 object 的指针更为实用。在这种情况下,Bar 为 Null,Foo 仍然独立存在。

例如,Foo 是汽车,Bar 是 driver。汽车可能有也可能没有 driver,但它仍然是汽车,因此汽车中的指针更有意义。此外,这允许更改汽车中的 driver,只需更改指向它的指针即可。

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

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

但是,如果 Bar 的构造函数需要参数,您仍然需要将其创建为 Foo.

的构造函数的初始化列表的一部分
class Foo: public QObject {
    Q_OBJECT
public:
    Foo(QObject *parent = nullptr) 
    : QObject(parent)
    , bar(arg1, arg2)
    { }
private:
    Bar bar;
};

更好的问题是:为什么通过 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 };
};