模型中虚拟索引实现的替代方案

时间:2016-04-27 20:02:42

标签: c++ qt templates qobject

所以我再次遇到了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的指针来调度索引的索引,所以不会内联索引的方法(我已经查看了用于确认这一点并尝试各种优化等无效的代码)。

如果没有动态分配索引并且没有对索引的虚拟调用,理想情况下,这个设计的替代方案是什么?

1 个答案:

答案 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中派生类!