为什么QList不是从QObject派生出来的?

Why is a QList not derived from a QObject?

本文关键字:派生 QObject QList 为什么      更新时间:2023-10-16

我想围绕QList创建一个QObservableCollection包装器(使用内部QList实现和转发所有调用,同时为更改集合的函数发出某种 CollectionsChanged 信号),但我看到QList不会从QObject继承。

我相信你需要从QObject继承才能发出Qt信号。所以我需要从QObject继承我的 QObeservableCollection。

但是QListQVector以及其他Qt集合不会继承QObject,所以我想它们一定是某种缺点或制作集合的问题。

我看到QSignalSpy继承了QObjectQList<QList<QVariant>>,所以也许他们只是没有看到从QObject继承的理由?

有一个非常重要的技术原因:moc无法处理模板,这对于通用容器类型来说几乎是必需的。

> QList 是一种值类型(如std::vector),它使用隐式共享,而 QObjects 必须用作指针并禁止复制

还有其他类反映了这种用法,例如 QImage

原因是简单的容器就像值一样,你有赋值运算符,你可以复制它们克隆等等。

QObject不能有这样的功能,它们是不可复制的。试想一下,当你创建具有连接的插槽和信号的对象克隆时会发生什么。这将导致完全混乱。克隆对象的子对象应该怎么做?也应该克隆吗?

另一件事是模板使用。类模板是一个QObject是moc工具的真正问题。

当然,你需要成为QObject才能发出信号,这是不正确的。您所需要的只是在某个地方有一个为您发出信号的QObject。如果你希望你的类可以直接传递给QObject::connect,你的类应该提供一个转换运算符来QObject*返回指向此类代理对象的指针。这完全回避了整个没有模板的moc喧嚣。

class FrobinatorObject : public QObject {
Q_OBJECT
Q_SIGNAL void frobSignal();
...
};
template <typename T> class Frobinator {
QScopedPointer<FrobinatorObject> m_proxy;
// Could be a QSharedPointer, depending on what semantics we want
...
public:
operator FrobinatorObject*() const { return m_proxy.data(); }
};
...
Frobinator<int> frob;
QObject::connect(frob, SIGNAL(frobSignal()), ...);
// or
QObject::connect(frob, &FrobinatorObject::frobSignal, ...);

另请注意,虽然模板参数化类中确实不能有信号或槽,但您当然可以将它们放在基类中,然后从中派生。基类可以处理类型删除的参数。所以:

// This won't work
template <typename T> class TemplateClass : public QObject {
Q_OBJECT
Q_SLOT void aSlot(const T *);
...
};
// But this certainly does work
class BaseClass : public QObject {
Q_OBJECT
Q_SLOT void aSlot(const void *);
...
}
template <typename T> class TemplateClass : public BaseClass {
void aMethod(const T * t) {
BaseClass::aSlot((const void*)&t);
}
...
}

TemplateClass还可以动态地将正确类型签名的插槽添加到BaseClass。虽然这需要对Qt的内部有一些了解,但对于一个应该是可重用的框架式类的类来说,它肯定是可以做到的。

虽然我无法了解开发人员的想法,但我会说根本没有必要。QList 是一个简单的容器。它应该保存元素,允许添加或删除它们,迭代它们等。

它不需要父母或孩子。无需立即使用信号或插槽。这是一个保持简单的问题。

如果您确实需要 QList 提供的功能之外的其他功能,那么实现起来很容易。但就一般情况而言,我想不要使事情过于复杂是一个合理和合乎逻辑的决定。

从 QObject 继承的额外开销对于大多数用途是不必要的。容器应尽可能小且尽可能快。

如果要从 QList 继承并为自己的类提供该功能,则可以这样做。