C ++设计问题:通过模板传递函数?

时间:2011-03-09 09:34:08

标签: c++ oop templates dynamic-cast

提前感谢您的阅读。这是一个挑剔的设计问题,因为我有一个足够的解决方案,但是问我是否有更好的方法通过模板,我不是很熟悉。

我需要存储一系列类型的数据表(派生自抽象类DataTable)。我编写了一个“DataTableIndex”抽象类,它存储了一个向量DataTable *,它处理所有DataTableIndexes常用的一般工作 - 执行查找,实现代理模式,以便只在需要时加载表,错误检查等。

然后我为每个表类型子类化,我需要这样做的唯一原因是因为每个表类型都有一个特定的函数来调用它来加载它。

如果可能的话,我想通过模板以某种方式避免使用DataTableIndex的子类,因为DataTable有很多子类。

class DataTableIndex
{  
  // various functions to implement lookup, Proxy and error checking
  //   functionality common to all DataTableIndexes.
  // This code needs access to _lookupTable and _theTables

  DataTable* getTable( int tableNum );

private:
  // These functions call the appropriate user interface function for loading
  //   a table of the subclass' type.
  //   They are called by more general non-virtual public functions
  virtual bool loadIndex( vector<LookupEntry*>& returnLookupTable ) = 0;
  virtual DataTable* loadTable( int tableNum ) = 0;

  vector<LookupEntry*> _lookupTable;
  vector<DataTable*>   _theTables;

  UserInterface* UI;
};  

这个类有非常简单的子类,它基本上指向User Interface类中实际打开和解析数据表文件的函数。

class TableTypeA_Index : public DataTableIndex
{
  virtual bool loadIndex( vector<LookupEntry*>& returnLookupTable )
  {
     return UI->loadTableAIndex( _lookupTable );
  }

  virtual DataTable* loadTable( int tableNum )  
  { 
     return UI->loadTableTypeA( _lookupTable[ tableNum ] );
  }

};

这很有效。但我觉得我应该能够通过模板参数将“loadTableTypeA”传递给DataTableIndex,所以我不必将其子类化。

我想使用模板的另一个原因是我不想一直将DataTable *转换为实际的表类型。即使我在编译时知道它应该是什么类型的表,我觉得我应该使用dynamic_cast&lt;&gt;错误检查,但我不希望getTable()的调用者每次都必须这样做(经常调用它)。

我的理想解决方案是:  1)将DataTableIndex类概括为模板,模板参数替换_lookupTable和_theTables中的LookupEntry *和DataTable *。这将消除铸造。

2)映射适当的UI函数以加载正确的表类型,而不进行子类化。

所以基本上我想使用这个类看起来像这样(某种程度上)

DataTableIndex< LookupEntryTypeAMatlab, 
                TableTypeA, 
                loadTableAIndex(),
                loadTableTypeA() > theTypeAIndex;

我对策略类有一些想法,但我对这种方法的印象是,在这种情况下,我只是将子类化移动到其他东西。

2 个答案:

答案 0 :(得分:1)

通常,可以使用strategy pattern来完成此操作。这可以使用简单的组合实现,因此不需要模板。但是,您仍需要为每种不同的表类型定义特殊策略。这将需要子类化:

class LoadTableStrategy {
public:
    virtual bool loadIndex( vector<LookupEntry*>& returnLookupTable ) = 0;
    virtual DataTable* loadTable( int tableNum ) = 0;
};

此类必须是每个表类型的子类。然后,DataTableIndex将在构造函数中接收LoadTableStrategy的实例,并使用它来加载数据,而不是私有虚函数。

您当然可以将策略类型作为模板参数传递,但请注意,这是一个缺点。使用不同模板参数实例化的两个DataTableIndex将是编译器的不同类型。除非您创建特定的重载,否则您将无法定义可以同时处理这两者的函数,或者将函数本身设置为函数模板。

答案 1 :(得分:0)

如果您想坚持使用模板,可以将TableTypeA_Index转换为模板,但是您必须将指针传递给UserInterface的成员函数到构造函数中。 E.g:

typedef bool (UserInterface::*LoadTableIndexFP) (vector<LookupEntry*>&);
typedef DataTable* (UserInterface::*LoadTableTypeFP) (LookupEntry*);

template<class TABLE>
class TableType_Index : public DataTableIndex
{
public:
    TableType_Index (LoadTableIndexFP loadTableIndexFP, LoadTableTypeFP loadTableTypeFP)
        : loadTableIndexFP (loadTableIndexFP)
        , loadTableTypeFP (loadTableTypeFP)
    {
    }

    virtual bool loadIndex( vector<LookupEntry*>& returnLookupTable )
    {
        return (UI->*loadTableIndexFP) (returnLookupTable);
    }

    virtual DataTable* loadTable( int tableNum )
    {
        return (UI->*loadTableTypeFP) (_lookupTable[ tableNum ]);
    }

private:
    LoadTableIndexFP loadTableIndexFP;
    LoadTableTypeFP loadTableTypeFP;
};

int main (int argc, char* argv[])
{
    TableType_Index<TableA> (&UserInterface::loadTableAIndex, &UserInterface::loadTableTypeA);

    return 0;
}

我没有添加LookupEntryTypeAMatlab作为模板参数,因为您的TableTypeA_Index定义不清楚它的作用是什么。

注意,将ptr-to-mem-funs传递给ctor的另一种方法是每个表类型都有一个traits类:

template<typename T>
struct TableTraits
{
};

template<>
struct TableTraits<TableA>
{
    static LoadTableIndexFP loadTableIndexFP;
    static LoadTableTypeFP loadTableTypeFP;
};

LoadTableIndexFP TableTraits<TableA>::loadTableIndexFP = &UserInterface::loadTableAIndex;
LoadTableTypeFP TableTraits<TableA>::loadTableTypeFP = &UserInterface::loadTableTypeA;

template<class TABLE>
class TableType_Index : public DataTableIndex
{
public:
    virtual bool loadIndex( vector<LookupEntry*>& returnLookupTable )
    {
        return (UI->*TableTraits<TableA>::loadTableIndexFP) (returnLookupTable);
    }

    virtual DataTable* loadTable( int tableNum )
    {
        return (UI->*TableTraits<TableA>::loadTableTypeFP) (_lookupTable[ tableNum ]);
    }
};

虽然这两种方法都不理想......