C++;:从模板参数继承类 我最近看到了下面的C++代码片段< /p> template <class B> class A : public B { ... }; 模板 A类:公共B类 { ... };
我想知道在哪种环境下这样的设计是好的做法 我的理解是,将超类作为模板参数允许a的用户在实例化a的对象时选择超类C++;:从模板参数继承类 我最近看到了下面的C++代码片段< /p> template <class B> class A : public B { ... }; 模板 A类:公共B类 { ... };,c++,templates,inheritance,design-patterns,C++,Templates,Inheritance,Design Patterns,我想知道在哪种环境下这样的设计是好的做法 我的理解是,将超类作为模板参数允许a的用户在实例化a的对象时选择超类 但是如果是这种情况,对所有用作模板参数且具有a扩展C的类(B)使用一个公共超类C不是更好吗?听起来像是包装类的一个很好的候选者: class base { public: virtual void f() = 0; }; class d1 : public base { public: virtual void f() override { ... }; }; class
但是如果是这种情况,对所有用作模板参数且具有a扩展C的类(B)使用一个公共超类C不是更好吗?听起来像是包装类的一个很好的候选者:
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;
}
类基{
公众:
虚空f()=0;
};
d1类:公共基地{
公众:
虚空f()覆盖{…};
};
d2类:公共基地{
公众:
虚空f()覆盖{…};
};
模板
类包装器:public T{
公众:
虚拟void f()覆盖{
pre_op();
T::f();
post_op();
}
私人:
void pre_op(){…}
void post_op(){…}
}
int main(int,char**){
包装物w1;
}
例如,包装类可以提供对派生类的同步访问。
它经常被用于所谓的“基于策略”的设计,即通过将所需的派生类组合起来,将特性添加到基类中,参见Andrei Alexandrescu的“现代C++设计:泛型编程和设计模式”。从中派生的实例化模板类称为“策略”。这种设计有时比继承更好,因为它允许组合策略,避免基于继承的模型中不可避免的组合爆炸
例如,请参见以下简单代码,其中红色
和蓝色
为笔
绘制策略:
#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;
}
#包括
#包括
结构红
{
std::string getColor()
{
返回“红色”;
}
};
结构蓝
{
std::string getColor()
{
返回“蓝色”;
}
};
模板
课堂:公共政策课
{
公众:
作废提款()
{
std::cout通常用于实现
用例包括:
一般来说,动态多态性可以带来好处,而不需要虚拟函数额外的运行时成本。但只有在编译时确定具体类型时,它才有用。我使用这种样式的地方是,我需要实现一个通用图库,它既易于使用,也易于维护
过了一段时间,我想到了这个设计:
GraphContainer、Edge、Node的抽象类:
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;
//...
};
模板
类GraphAbstractContainer
{
公众:
使用Node=T1;
使用Edge=T2;
虚拟std::list getConnectedNodes(const节点和节点)const=0;
虚拟节点addNode(const Node&)=0;
//...
};
类GraphAbstracthNode
{
公众:
虚拟uint32_t getId()常量=0;
虚拟void setID(uint32_t id)=0;
//..
};
模板
类GraphAbstractEdge
{
公众:
使用Node=T;
//GraphAbstractEdge(){}
虚拟节点firstNode()常量=0;
虚拟节点secondNode()常量=0;
虚空setFirstNode(常量节点和节点)=0;
虚拟void setSecondNode(常量节点和节点)=0;
//...
};
然后我通过直接从模板参数继承添加Adj_列表和Adj矩阵实现
例如,My Adj List 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:
//...
};
模板
类GraphAdjListContainer:公共T1
{
公众:
使用Node=typename T1::Node;
使用Edge=typename T1::Edge;
//返回连接的节点
虚拟标准::列出getConnectedNodes(常量节点和节点)常量
{
//..
}
//..
};
};
模板
类GraphAdjNode:public T
{
公众:
//正在实现抽象类方法。。。
};
模板
类graphhadjedge:public T
{
公众:
//...
};
我的Graph类也直接从模板继承:
template<class GraphContainer=GraphAdjListContainer<>>
class Graph :public GraphContainer
{
public:
using Node = typename GraphContainer::Node;
using Edge = typename GraphContainer::Edge;
//...
}
模板
类图形:公共图形容器
{
公众:
使用Node=typename GraphContainer::Node;
使用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_;
}
class三节点:公共图形节点
{
公众:
//...
std::字符串字;
};
三类
{
公众:
使用Graph=Graph;
使用Node=Graph::Node;
使用Edge=Graph::Edge;
无效添加字(wstring字);
//...
私人:
图;
}
通常情况下是相反的。但您显示的模式可用于策略或接口注入。@πάνταῥεῖ: 设计模式与具体的用例无关。@user695652不应该这样,如果它真的关闭了,我会重新投票给你。@πάνταῥεῖ: 设计模式没有动机部分。关于它们的书籍或文章有动机部分,但没有动机部分。你是在暗示每一个缺乏示例的抽象问题都过于宽泛吗?事实上,我有时会收到错误的问题答案,因为我添加了示例;也就是说,我得到的答案是针对我的示例而不是问题的,所以跳过了一次考试le有时实际上更好。好的,我收回了我的投票。我渴望阅读复杂的答案;)继承和拥有相同类型的成员似乎是一种浪费,为什么不调用父成员函数T::f()
?这一点很好,但您的示例并不能证明继承是正确的。它可能是组合的。@Ernon这是一个简单的示例,但在更现实的场景中,您希望编写基类以便它与任何策略一起工作,这就是为什么(据我所知),您从临时