class A : public QObject {
A() : b(this) {} // !
class B : public QObject {
B(QObject* parent)
};
B b;
}
如果子对象不是动态的,我应该设置父级吗?这两种情况有什么区别?
什么更好,有什么区别?
class A : public QObject {
A() : b(new B(this)) {} // !
class B : public QObject {
B(QObject* parent)
};
B* b;
}
或
class A : public QObject {
A() : b(new B()) {} // !
class B : public QObject {
B()
};
smart_ptr<B> b;
}
答案 0 :(得分:2)
QObject
亲子关系和存储持续时间是正交问题,应单独解决。
除非所有拥有的对象都将所有者设置为父级,否则您无法将其他QObject
的{{1}}移动到另一个线程。因此,通过不设置父级,您过早地限制了父类的功能。
在现代C ++中,具有动态存储持续时间的子QObject
应该通过资源管理器保存。这样的管理器可以是智能指针,也可以是拥有对象。回想一下,QObject
也是一个QObject
容器。因此,甚至没有必要有一个指向子对象的显式成员指针 - 比如你是否只需要在构造所有者期间直接引用该对象。
一般来说,通过让任何成员具有动态存储持续时间,你会过早地处于悲观状态,除非他们的构造函数非常昂贵并且你想要将构造延迟到稍后的时间。
所以,如果你没有使用PIMPL idiom,那么所有成员都应该在对象本身中拥有自动存储持续时间:
QObject
否则,它们属于PIMPL:
// A.h
class A : public QObject {
Q_OBJECT
QSerialPort m_port { this };
QThread m_thread { this };
....
};
还应该指出,使用指向前向声明的不完整类型的指针以“加速”编译或“减少依赖性”是一种反模式。如果您愿意在头文件中公开实现细节,只需按值保存所有成员 - 如上面的第一个示例所示。如果您担心过多的依赖关系并希望隐藏您的实现,请使用PIMPL。指向前向声明类的指针的“中间道路”会强加额外的动态存储分配,因此是一种不灵活的怪物,无助于任何人,也没有任何人。
// A.h
class APrivate;
class A : public QObject { ... };
// A.cpp
#include "A.h"
class APrivate {
public:
A * const q_ptr;
QSerialPort m_port { q_ptr };
QThread m_thread { q_ptr };
APrivate(A * q) : q_ptr(q) {}
};
答案 1 :(得分:1)
父/子机制不仅仅是在父对象被销毁时删除对象。例如,子对象将始终在与其父对象相同的线程上运行,如果父对象线程发生更改,则子对象也将移动。但只是众多例子中的一个。如果您使用smart_ptr<B> b
,则不会拥有这些功能。 Qt中的等价物为QScopedPointer
。因此,如果可能的话,最好使用QObject父/子机制。有关整个QObject-parent-child-mechanism的更多信息,请参阅Object Trees & Ownership
关于指针或堆栈之间的区别:大多数QObject都不可复制,不能“移动分配”。在这种情况下,您将需要指针“交换”子对象。但除此之外,它更像是一种风格问题。我个人更喜欢指针式方法。即使您使用的是“堆栈版”,您仍然可以传递父级,以利用所有其他功能。
但最重要的是:似乎你忘了将父传递给QObject构造函数了! (如果你把它留下来简化代码块,请纠正我)。如果你不这样做,你的对象将不会成为它的父母的孩子:
class A : public QObject {
A(QObject *parent = NULL) :
QObject(parent),//Here, because A may become a child of another object some time
b(new B(this))
{}
class B : public QObject {
B(QObject *parent = NULL) :
QObject(parent)//And here it's neccessary
{}
};
B* b;
}