模型中虚拟索引实现的替代方案
Alternatives to virtual index implementation in a model
所以我又遇到了QObject
不能和模板混用的限制(至少不能直接混用)。基本上我有一个代理模型 class,它使用索引将源位置转换为本地位置并返回。索引可以通过多种方式实现,目前我需要两个版本,一个使用 QHash
,一个使用 QVector
。索引的界面对两者都是通用的,只有在索引操作方面存在细微差别。使用模板这很容易,我将 class 设为模板,然后对这两种情况使用专门化。
但是模型需要是 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
的指针将调用分派到索引,因此不会内联索引的任何方法(我查看了反汇编代码以确认这一点并尝试了各种优化等无济于事) .
在没有索引动态分配和虚拟调用索引的情况下,这种设计的理想替代方案是什么?
使索引类型特定 class 成为基础 classes:
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
更广泛或不同的接口,您当然需要编写这样一个基础 class 并在具体实现中从中派生。
如果 IndexHandler
需要,您可以使用 CRTP 直接调用派生的 class 方法,使其更小:
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
};
您还可以 "promote" 基础 class 方法作为 Qt 插槽:
class VectorIndexProxy : ... {
#ifdef Q_MOC_RUN
Q_SLOT void foo();
#endif
};
参见 this question 关于具有信号和槽的基础非 QObject class。
最后,您可以使用 PIMPL idiom,并根据需要具体实现固定类型。工厂将在构造函数中调用,您将在不同的 PIMPL 中交换不同的索引。这并不像你想象的那么昂贵,因为 所有 Qt classes 已经动态分配了一个 PIMPL,所以你可以通过从 QObjectPrivate
派生你的 PIMPL 来搭载该分配] (#include <private/qobject_p.h>
),并将 PIMPL 的实例传递给受保护的 QObject(QObjectPrivate&)
。这种模式在 Qt 中无处不在,因此即使它是一个实现细节,它至少不会在 Qt 5 中消失。这是一个粗略的草图:
// 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)
{}
};
如果您从 QAbstractItemModel
派生,您的 PIMPL 将以相同的方式从 QAbstractItemModelPrivate
派生;这适用于 Qt 中的任何 QObject
-派生 class!
所以我又遇到了QObject
不能和模板混用的限制(至少不能直接混用)。基本上我有一个代理模型 class,它使用索引将源位置转换为本地位置并返回。索引可以通过多种方式实现,目前我需要两个版本,一个使用 QHash
,一个使用 QVector
。索引的界面对两者都是通用的,只有在索引操作方面存在细微差别。使用模板这很容易,我将 class 设为模板,然后对这两种情况使用专门化。
但是模型需要是 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
的指针将调用分派到索引,因此不会内联索引的任何方法(我查看了反汇编代码以确认这一点并尝试了各种优化等无济于事) .
在没有索引动态分配和虚拟调用索引的情况下,这种设计的理想替代方案是什么?
使索引类型特定 class 成为基础 classes:
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
更广泛或不同的接口,您当然需要编写这样一个基础 class 并在具体实现中从中派生。
如果 IndexHandler
需要,您可以使用 CRTP 直接调用派生的 class 方法,使其更小:
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
};
您还可以 "promote" 基础 class 方法作为 Qt 插槽:
class VectorIndexProxy : ... {
#ifdef Q_MOC_RUN
Q_SLOT void foo();
#endif
};
参见 this question 关于具有信号和槽的基础非 QObject class。
最后,您可以使用 PIMPL idiom,并根据需要具体实现固定类型。工厂将在构造函数中调用,您将在不同的 PIMPL 中交换不同的索引。这并不像你想象的那么昂贵,因为 所有 Qt classes 已经动态分配了一个 PIMPL,所以你可以通过从 QObjectPrivate
派生你的 PIMPL 来搭载该分配] (#include <private/qobject_p.h>
),并将 PIMPL 的实例传递给受保护的 QObject(QObjectPrivate&)
。这种模式在 Qt 中无处不在,因此即使它是一个实现细节,它至少不会在 Qt 5 中消失。这是一个粗略的草图:
// 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)
{}
};
如果您从 QAbstractItemModel
派生,您的 PIMPL 将以相同的方式从 QAbstractItemModelPrivate
派生;这适用于 Qt 中的任何 QObject
-派生 class!