哪个更好?如果子对象是类的成员,则为子对象设置父对象

What is better? Setup QObject parent for child or not if child is member of class?

本文关键字:对象 成员 设置 更好 如果      更新时间:2023-10-16
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;
}

QObject亲代与保存期是正交关系,应分别处理

你不能将持有其他QObjectQObject移动到另一个线程,除非所有拥有的对象都将所有者设置为父对象。因此,如果不设置父类,就过早地限制了父类的功能。

在现代c++中,具有动态存储时间的子QObject应该通过资源管理器保存。这样的管理器可以是一个智能指针,或者只是拥有对象。回想一下,QObject也隐式地是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) {}
};

还应该指出,为了"加速"编译或"减少依赖"而使用指向前向声明的不完整类型的指针是一种反模式。如果您愿意在头文件中公开实现细节,只需按值保留所有成员即可——如上面的第一个示例所示。如果您担心过多的依赖关系并希望隐藏您的实现,请使用PIMPL。指针指向前向声明类的"中庸之道"会给您带来额外的动态存储分配,因此是一个不灵活的怪物,对任何事情都没有帮助。

// DO NOT WRITE CODE LIKE THIS!!
class QSerialPort;
class QThread;
class A : public QObject {
  Q_OBJECT
  QSerialPort * m_port;
  QThread * m_thread;
  ...
};
// DO NOT WRITE CODE LIKE THIS!!

父/子机制不仅仅是在它的父类被销毁时删除对象。例如,子对象将始终在与其父线程相同的线程上运行,如果父线程更改,子对象也将移动。但这只是众多例子中的一个。如果您使用的是smart_ptr<B> b,则不会具有这些功能。Qt中与此等价的是QScopedPointer。因此,如果可能的话,最好使用QObject父/子机制。关于整个qobject -parent-child机制的更多信息,请参见对象树。所有权

关于指针和堆栈之间的区别:大多数qobject是不可复制的,不能"移动赋值"。在这种情况下,您将需要指针来"交换"子对象。但除此之外,这更多的是一个风格问题。我个人更喜欢指针方法。即使你使用的是"堆栈版本",你仍然可以传递父版本,以利用所有其他功能。

但最重要的是:您似乎忘记将parent传递给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;
}