所以我再次遇到了QObject
s的限制,这些限制不能与模板混合(至少不能直接)。基本上我有一个代理模型类,它使用索引将源位置转换为本地位置并返回。索引可以通过多种方式实现,现在我需要两个版本,一个使用QHash
,另一个使用QVector
。索引的接口对于两者都是共同的,只有关于索引操作的细微差别。使用模板这很容易,我会将类作为模板,然后对这两种情况使用专门化。
然而,模型需要是QObject
,所以相反我似乎需要像这样使用多态:
class IndexInterface;
class VectorIndex; //inherits IndexInterface
class HashIndex; //inherits IndexInterface
class ProxyModel : public QObject
{
Q_OBJECT
public:
enum IndexType { Vector, Hash };
explicit ProxyModel(IndexType indexType, QObject *parent = 0) :
QObject(parent),
index(indexType == Vector ? new VectorIndex : new HashIndex)
{
}
//...
private:
IndexInterface *index = nullptr;
};
我有几个问题。首先,它需要动态分配索引,我想摆脱它。其次,由于使用了指向IndexInterace
的指针来调度索引的索引,所以不会内联索引的方法(我已经查看了用于确认这一点并尝试各种优化等无效的代码)。
如果没有动态分配索引并且没有对索引的虚拟调用,理想情况下,这个设计的替代方案是什么?
答案 0 :(得分:2)
使索引类型特定的类成为基类之一:
template <typename Index> class IndexHandler {
};
using VectorIndexHandler = IndexHandler<QVector>;
using HashIndexHandler = IndexHandler<QHash>;
class VectorIndexProxy : public QAbstractItemModel, VectorIndexHandler {
... // should be very small
};
class HashIndexProxy : public QAbstractItemModel, HashIndexHandler {
... // should be very small
};
然后使用工厂函数
而不是将索引类型传递给构造函数QAbstractItemModel * proxyFactory(IndexType indexType, QObject * parent = 0) {
switch (indexType) {
case Foo::Vector:
return new VectorIndexProxy(parent);
...
}
}
如果您想要一个比QAbstractItemModel
更广泛或不同的接口,您当然需要编写这样一个基类并从具体实现中派生出来。
如果IndexHandler
需要,您可以使用CRTP直接调用派生类的方法,使其更小:
template <typename Index, typename Derived> class IndexHandler {
Derived * derived() { return static_cast<Derived*>(this); }
const Derived * derived() const; // as above
void foo() {
derived()->setObjectName("Yay");
}
};
class VectorIndexProxy :
public QAbstractItemModel,
public VectorIndexHandler<QVector, VectorIndexProxy>
{
... // should be very small
};
你也可以推广&#34;从基类到Qt槽的方法:
class VectorIndexProxy : ... {
#ifdef Q_MOC_RUN
Q_SLOT void foo();
#endif
};
请参阅this question有关具有信号和插槽的基本非QObject类。
最后,您可以使用PIMPL idiom,并按照您的需要具有固定类型的具体实现。将在构造函数中调用工厂,并且您将针对不同的索引交换不同的PIMPL。这并不像您想象的那么昂贵,因为所有Qt类已经动态地分配了一个PIMPL ,因此您可以通过从QObjectPrivate
({{{{{}}派生您的PIMPL来捎带该分配。 1}}),并将PIMPL的实例传递给受保护的#include <private/qobject_p.h>
。这种模式在Qt中无所不在,所以即使它是一个实现细节,它至少也不会在Qt 5中消失。这是一个草图:
QObject(QObjectPrivate&)
如果您派生自// ProxyModel.cpp
#include <private/qobject_p.h>
class ProxyModelPrivate : public QObjectPrivate {
// Note: you don't need a q-pointer, QObjectData already provides it
// for you! CAVEAT: q-pointer is not valid until the QObject-derived-class's
// constructor has returned. This would be the case even if you passed
// the q-pointer explicitly, of course.
...
}; // base class
class VectorProxyModelPrivate : public ProxyModelPrivate { ... };
class ProxyModel : public QObject
{
Q_OBJECT
Q_DECLARE_PRIVATE(ProxyModel)
ProxyModel * pimpl(IndexType indexType) {
switch (indexType) {
case Vector: return new VectorProxyModelPrivate();
...
}
public:
enum IndexType { Vector, Hash };
explicit ProxyModel(IndexType indexType, QObject *parent = 0) :
QObject(*pimpl(IndexType), parent)
{}
};
,您的PIMPL将以同样的方式从QAbstractItemModel
派生;这适用于任何QAbstractItemModelPrivate
- 在Qt中派生类!