为什么QList没有派生自QObject?

3
我想创建一个QObservableCollection的wrapper,它可以包装一个QList(使用内部的QList作为实现并转发所有调用,同时发出一些改变集合的信号),但我发现QList没有继承QObject。
我认为你需要继承QObject才能发出Qt信号。因此,我的QObeservableCollection需要继承QObject。
但是,QList、QVector和其他Qt集合都没有继承自QObject,所以我想可能存在某种缺陷或问题使得不能创建集合。
我注意到QSignalSpy同时继承自QObject和QList>,所以也许他们只是没看到继承自QObject的理由?
6个回答

5

有一个非常重要的技术原因: moc 无法处理模板,而泛型容器类型需要使用模板。


2
有解决方法(例如使用抽象的QObject超类声明所有信号和槽,并创建一个未声明Q_OBJECT宏但实现它们的模板子类)。 - ratchet freak
1
这不是一个很好的理由。你真的不想在每个容器类型实例中都有一个 QObject 的开销,更不用说 QObject 是不可复制的了! - Kuba hasn't forgotten Monica
@KubaOber 不是什么理由?!解决这个问题非常麻烦。 - cmannett85
1
@cmannett85:看我的回答。到目前为止,我发现在实践中这根本不重要。 - Kuba hasn't forgotten Monica

4

QList被设计为一种值类型(类似于std::vector),它使用隐式共享,而QObjects必须用作指针并禁止复制。

还有其他类与此类似,例如QImage。


3

原因很简单,容器就像值一样,你有赋值操作符,可以复制克隆等。但是QObject并不具备这样的功能,它们无法被复制。试想一下,当您创建连接了插槽和信号的对象的克隆时会发生什么情况,它会导致混乱。克隆对象的子对象应该怎么处理?是否也应该被克隆?

另一个问题是模板的使用。类模板是QObject时会成为 moc 工具的真正问题。


3

并不是说你必须成为一个 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内部的理解,但对于一个被设计成可重用的框架式类来说,这肯定是可行的。

2
虽然我不能深入了解开发者的想法,但我认为他们认为没有必要。QList是一个简单的容器,应该包含元素,允许添加或删除元素,对它们进行迭代等等。
它不需要父级或子级。没有立即需要信号或插槽。这是保持简单的问题。
如果您确实需要超出QList提供的附加功能,那么很容易实现。但就一般情况而言,不过度复杂化似乎是合理和逻辑的决定。

1
继承自QObject会增加额外的开销,在大多数情况下都是不必要的。容器应该尽可能地小和快。
如果您想从QList继承并为自己的类提供该功能,可以这样做。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接