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 时自动释放。
可以说,决定走哪条路取决于 Foo
和 Bar
的实际情况。
当 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 };
};
我们通常像这样编写拥有其他 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 时自动释放。
可以说,决定走哪条路取决于 Foo
和 Bar
的实际情况。
当 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 };
};