C ++:从template参数继承类

时间:2014-05-26 11:09:06

标签: c++ templates inheritance design-patterns

我最近看到了以下C ++代码片段

template <class B>
class A : public B
{
   ...
};

我想知道哪种设置这样的设计是好的做法?

我理解的方式是,将超类作为模板参数允许 A 的用户在实例化 A 的对象时选择超类。

但是如果是这种情况,那么为所有类( B )建立一个共同的超类 C 会更好。模板参数并 A 扩展 C

4 个答案:

答案 0 :(得分:13)

它通常用于实现static polymorphism

用例是:

通常,您可以从动态多态中获益,而无需虚拟功能的额外运行时成本。但只有在编译时才能确定具体类型,它才有用。

答案 1 :(得分:4)

听起来像包装类的好候选人:

class base {
public:
  virtual void f() = 0;
};

class d1 : public base {
public:
  virtual void f() override { ... };
};

class d2 : public base {
public:
  virtual void f() override { ... };
};

template <typename T>
class wrapper : public T {
public:
  virtual void f() override {
    pre_op();
    T::f();
    post_op();
  }

private:
  void pre_op() { ... }
  void post_op() { ... }
}

int main(int, char**) {
  wrapper<d1> w1;
}

例如,包装类可以提供对派生类的同步访问。

答案 2 :(得分:1)

它经常用于所谓的基于政策的&#34;设计,即通过具有所需派生类的组合向基类添加特性,请参阅&#34;现代C ++设计:应用的通用编程和设计模式&#34;作者:Andrei Alexandrescu。您派生的实例化模板类称为&#34;策略&#34;。这种设计有时比继承更好,因为它允许组合策略并避免基于继承的模型中不可避免的组合爆炸。

例如,请参阅以下简单代码,其中REDBLUEPen的绘图政策:

#include <iostream>
#include <string>

struct RED
{
    std::string getColor()
    {
        return "RED";
    }
};

struct BLUE
{
    std::string getColor()
    {
        return "BLUE";
    }
};

template <typename PolicyClass>
class Pencil: public PolicyClass
{
public:
    void Draw()
    {
        std::cout << "I draw with the color " << PolicyClass::getColor() << std::endl; 
    }
};


int main()
{   
    Pencil<RED> red_pencil; // Drawing with RED
    red_pencil.Draw();
    Pencil<BLUE> blue_pencil; // Different behaviour now
    blue_pencil.Draw();

    return 0;
}

可以在这里阅读更多内容: http://en.wikipedia.org/wiki/Policy-based_design

答案 3 :(得分:1)

我使用这种风格的地方是我需要实现一个易于使用且易于维护的通用图形库的地方 过了一会儿,我带来了这个设计:

GraphContainer,Edge,Node的ABstract类:

template < class T1,class T2>
class  GraphAbstractContainer
{
public:
    using Node = T1;
    using Edge = T2;
    virtual std::list<Node> getConnectedNodes(const Node& node)const = 0;
    virtual Node addNode(const Node&) = 0;
    //...
};

class  GraphAbstracthNode
{
public:
    virtual uint32_t getId() const = 0;
    virtual void setID(uint32_t id)=0;
    //..
};

template<class T>
class  GraphAbstractEdge
{
public:
    using Node = T;
    //GraphAbstractEdge(){}
    virtual Node  firstNode() const = 0;
    virtual Node   secondNode() const = 0;
    virtual void  setFirstNode(const Node& node)  = 0;
    virtual void  setSecondNode(const Node& node) = 0;
    //...

};

然后我通过直接从模板参数继承来添加Adj_List和Adj Matrix实现。

例如我的调整列表classess看起来像这样:

template<class T1 = GraphAbstractContainer<GraphAdjNode,
                   GraphAdjEdge>>
class  GraphAdjListContainer : public T1
{
public:
    using Node = typename T1::Node;
    using Edge = typename T1::Edge;

    //return connected Nodes
    virtual std::list<Node> getConnectedNodes(const Node& node) const
    {
        //..
    }
    //..
  };

};

template<class T>
class  GraphAdjNode : public T
{
public:
    //implementing abstract class methods...
};

template<class T>
class  GraphAdjEdge : public T
{
public:
   //...

};

此外,My Graph类也直接从模板继承:

template<class GraphContainer=GraphAdjListContainer<>>
    class   Graph :public  GraphContainer
    {
    public:
        using Node = typename GraphContainer::Node;
        using Edge = typename GraphContainer::Edge;
         //...

}

这种设计模式的一个优点是你可以简单地通过从抽象类继承并填充模板参数来改变整个类底层的东西。

例如,我只需执行以下操作即可定义Trie数据结构:

class TrieNode :public GraphAdjNode
{
public:
    //...
    std::string word_;
};

class Trie 
{
public:
    using Graph = Graph < ecv::GraphAdjListContainer<TrieNode, ecv::GraphAdjListEdge<TrieNode>>>;
    using Node =  Graph::Node;
    using Edge =  Graph::Edge;
    void addWord(wstring word);
    //...
private:
    Graph graph_;
}