本次要讲的是如何通过泛型函数来简化我们的程序。
  泛型函数除了之前介绍的一些优点外还有两个重要的优点

1.消除重复逻辑,提高程序的内聚性和健壮性

  泛型函数在某种程度上用来弥补泛型类型的不足。通过泛型类型T可以消除一些重复代码,当不同的类型具有相同的行为时,通过泛型擦除类型,达到消除重复简化代码的目的。例如:

template<typename T>
auto Add(T a, T b)->decltype(a+b)
{
return a + b;
} void TestAdd()
{
auto r1 = Add(, ); //
auto r2 = Add(1.2, 3.5); //4.7
auto r3 = Add(string("aa"), string("bb")); //aabb
}

  我们可以通过一个泛型Add函数对不同的类型:int、double和string做加运算,而不用针对每种类型写一个Add函数,复用了Add函数,消除了重复。本质 上通过T擦出了类型,使得我们可以复用这些不同类型的共同行为。也就是说T可以消除类型不同而行为相同导致的重复,这在很多时候可以有效的消除重复代码,但是有时候却不能消 除某些函数的重复逻辑。比如,两个函数中大部分逻辑都是相同的,只是某个处理步骤略有不同时就无能为力了。也就是说大部分逻辑相同,只是某个行为有差异,只是通过泛型类型T就帮不上忙了,因为泛型类型T只能擦除类型,而不能屏蔽不同的行为。比如:

//遍历xml文件,将标签IDAttr的值和AlgorithmTypeAttr属性值组成键值对,存到map中
void ParseA(const string& confFile, const string& xmlPath,unordered_multimap<uint32_t, int>& map)
{
property_tree::ptree ptree;
property_tree::read_xml(confFile, ptree);
auto kpichilds = ptree.get_child(xmlPath);
std::for_each(kpichilds.begin(), kpichilds.end(), [this, &map](const ptree::value_type& pair)
{
string str = pair.second.get<string>(IDAttr);
uint32_t key = strtol(str.data(), nullptr, ); int val = pair.second.get<int>(AlgorithmTypeAttr);
map.insert(make_pair(key, val));
});
} //遍历xml文件,将标签AlgorithmAttr属性值和其子节点的标签Attr属性值组成键值对,存入map中
void ParseB(const string& confFile, const string& xmlPath,unordered_multimap<uint32_t, string>& map)
{
property_tree::ptree ptree;
property_tree::read_xml(confFile, ptree);
auto kpichilds = ptree.get_child(xmlPath);
std::for_each(kpichilds.begin(), kpichilds.end(), [&map](const ptree::value_type& pair)
{
int type = pair.second.get<int>(AlgorithmAttr);
auto childs = pair.second.get_child(Tag);
for (auto item : childs)
{
bap.insert(make_pair(type, item.second.get<string> (Attr)));
}
});
}

  这两个函数大部分逻辑都是相似的,只是在最后组成键值对时稍有差异,这两个函数中存在重复代码和重复逻辑(遍历生成键值对),重复代码比较好消除,但是重复逻辑该如何消除呢?这时我们就需要通过泛型的函数来屏蔽行为的差异,使得逻辑保持一致,各处仍然可以复用该逻辑代码。重构后代码是这样的:

//通过F屏蔽细小的行为差异,使得各个函数可以复用这段代码
template<typename F>
void Parse(const string& confFile, const string& xmlPath, const F& f)
{
property_tree::ptree ptree;
property_tree::read_xml(confFile, ptree);
auto kpichilds = ptree.get_child(xmlPath);
std::for_each(kpichilds.begin(), kpichilds.end(), f);
} //内部复用Parse逻辑
void ParseA(unordered_multimap<uint32_t, int>& map)
{
Parse(ConfFileName, KPIPath, [this, &map](const ptree::value_type& pair)
{
string str = pair.second.get<string>(IDAttr);
uint32_t key = strtol(str.data(), nullptr, ); int val = pair.second.get<int>(AlgorithmTypeAttr);
map.insert(make_pair(key, val));
};);
} //内部复用Parse逻辑
void ParseA(unordered_multimap<uint32_t, string>& map)
{
Parse(ConfFileName, KPIPath, [this, &map](const ptree::value_type& pair)
  {
  int type = pair.second.get<int>(AlgorithmAttr);
  auto childs = pair.second.get_child(Tag);
  for (auto item : childs)
  {
  bap.insert(make_pair(type, item.second.get<string>(Attr)));
  }
  };);
}

  重构后,细微的行为差异通过泛型的函数F来屏蔽了,具体的有差异的行为在外面定义,从而获得了逻辑上的一致,并且增强了灵活性。也许有人觉得如此费心思去消除差异来重用一段逻辑代码未免有点过头了。其实不然,高质量的代码就是要消除重复逻辑,应该所有的逻辑只在一个地方制定,尽可能多的复用已有逻辑。消除重复逻辑也可以使得我们的代码具有较高的内聚性,可以做到一个bug只修改一个地方,也间接的让外面的调用接口都充当了测试函数,提高了程序的健壮性。关于消除重复逻辑的好处不多说了。让我们看看泛型函数如何实现非继承的多态调用吧。

2.非继承的多态调用

  通过泛型函数可以不需继承就可以实现多态,因为F可以在外面定义,所以你可以调用相同的接口而实现不同的行为,达到多态调用的目的。比如:有一个聚合算法接口,通过派生分别实现加和减。

struct IAggregate
{
  virtual int Aggregate(int x, int y) = ;
}; struct Add : public IAggregate
{
  int Aggregate(int x, int y)
  {
    return x+y;
  }
}; struct Sub : public IAggregate
{
  int Aggregate(int x, int y)
  {
    return x-y;
  }
}; void TestAggregate()
{
  //加法
  shared_ptr<IAggregate> ptrAdd(new Add);
  ptrAdd->Aggregate(,);   //减法
  shared_ptr<IAggregate> ptrSub(new Sub);
  ptrSub->Aggregate(,);
}

  通过泛型函数的写法是这样的:

template<typename T, typename F>
auto Aggregate(const T& x, const T& y, F& f)->decltype(f(x,y))
{
  return f(x,y);
} void TestAggregate()
{
  //加法
  Aggregate(,, [](int x, int y){return x+y;});   //减法
  Aggregate(,, [](int x, int y){return x-y;});
}

  我们可以看到通过泛型函数实现多态调用更简洁,消除了继承的强耦合,也提高了性能。

  至此,c++11改进我们的程序之简化我们的程序系列的介绍告一段落了,接下来,会介绍c++11如何优化我们的程序性能以及c++11工程实践系列,敬请关注。

  c++11 boost技术交流群:296561497,欢迎大家来交流技术。

(原创)C++11改进我们的程序之简化我们的程序(八)的更多相关文章

  1. (原创)C++11改进我们的程序之简化我们的程序(二)

    这次要讲的是:C++11如何通过组合函数来简化我们的程序.关于组合函数,大家可能对这个概念有点陌生.组合函数是将N个一元函数组成一种更复杂的函数,每个函数的返回值作为参数传给下一个函数,直到传到最后一 ...

  2. C++11改进我们的程序之简化我们的程序1

    C++11改进我们的程序之简化我们的程序(一) C++11在很多方面可以简化我们的程序开发,我会在“简化我们的程序”这一系列的博文中一一讲到,敬请关注.这次要讲的是:C++11如何通过获取函数模板的返 ...

  3. (原创)C++11改进我们的程序之简化我们的程序(四)

    这次要讲的是:c++11统一初始化.统一begin()/end()和for-loop循环如何简化我们的程序 初始化列表 c++11之前有各种各样的初始化语法,有时候初始化的时候还挺麻烦,比较典型的如v ...

  4. (原创)C++11改进我们的程序之简化我们的程序(三)

    这次要讲的是:C++11如何通过auto.decltype和返回值后置来简化我们的程序. auto和c#中的var类似,都是在初始化时自动推断出数据类型.当某个变量的返回值难于书写时,或者不太确定返回 ...

  5. (原创)C++11改进我们的程序之简化我们的程序(一)

    C++11在很多方面可以简化我们的程序开发,我会在“简化我们的程序”这一系列的博文中一一讲到,敬请关注.这次要讲的是:C++11如何通过获取函数模板的返回值类型来简化我们的程序.在谈到简化之前,我们先 ...

  6. (原创)c++11改进我们的模式之改进代理模式,实现通用的AOP框架

    c++11 boost技术交流群:296561497,欢迎大家来交流技术. 本次要讲的时候如何改进代理模式,具体来说是动态代理模式,动态代理模式一般实现AOP框架,不懂AOP的童鞋看这里.我前面的博文 ...

  7. (原创)c++11改进我们的模式之改进命令模式

    模式虽然精妙,却难完美,比如观察者模式中观察者生命周期的问题:比如访问者模式中循环依赖的问题等等:其它很多模式也存在这样那样的一些不足之处,如使用场景受限.实现复杂.不够简洁.不够通用等.但我觉得不足 ...

  8. (原创)c++11改进我们的模式之改进访问者模式

    本次讲c++11改进我们的模式之改进访问者模式 访问者模式是GOF23个设计模式中比较复杂的模式之一,但是它的功能也很强大,非常适合稳定的继承层次中对象的访问,可以在不修改被访问对象的情况下,动态添加 ...

  9. (原创)C++11改进我们的程序之简化我们的程序(七)

    这次要讲的内容是:c++11中的tuple(元组).tuple看似简单,其实它是简约而不简单,可以说它是c++11中一个既简单又复杂的东东,关于它简单的一面是它很容易使用,复杂的一面是它内部隐藏了太多 ...

随机推荐

  1. Oracle的悲观锁和乐观锁

    为了得到最大的性能,一般数据库都有并发机制,不过带来的问题就是数据访问的冲突.为了解决这个问题,大多数数据库用的方法就是数据的锁定. 数据的锁定分为两种方法,第一种叫做悲观锁,第二种叫做乐观锁.什么叫 ...

  2. numpy中的argpartition

    numpy.argpartition(a, kth, axis=-1, kind='introselect', order=None) 在快排算法中,有一个典型的操作:partition.这个操作指: ...

  3. Delphi消息推送

    移动端的消息推送大家都体验过,智能手机上一大堆广告等各种消息会不时从消息栏中弹出来骚扰你. PC程序中我们有时也会用到消息推送,比如通知之类.通常我们使用的方法可能更多地使用Socket之类来处理,有 ...

  4. 是否只查看安全传送的网页内容? 去掉 IE弹出窗口

    选择IE工具intemt选项,在选项卡里选择安全,然后在安全选项卡里点自定义级别,在设置里找到‘其他’这个分类,在次分类下找到‘显示混合内容’选择‘启用’然后保存退出就OK了,当然楼上几位说安全问题, ...

  5. C语言字符串

    C语言字符串 一.字符串基础 注意:字符串一定以\0结尾. Printf(“yang\n”); 其中yang为字符串常量,“yang”=‘y’+‘a’+‘n’+‘g’+‘\0’.字符串由很多的字符组成 ...

  6. 如何查看出口IP地址?

    出口ip地址怎么看?#curl ifconfig.me

  7. 【javascript】javascript中function(){},function(){}(),new function(){},new Function()

    和java比起来,javascript真的是松散的无以复加,不过这也让我们在无聊之余,有精力去探讨一些复杂的应用,从而在开发之路上,获得一些新的想法. javascript中的类的构造 javascr ...

  8. easyui按钮linkbutton 不可用(置灰)与 可用(取消置灰)

    easyui linkbutton按钮: 不可用(置灰) $('#butFree').linkbutton('disable'); 可用(取消置灰) $('#butFree').linkbutton( ...

  9. MySQL Cluster

    MySQL Cluster MySQL集群一个非共享(shared nothing).分布式.分区系统,使用同步复制机制提供高可用和高性能. MySQL集群使用的是NDB引擎.NDB存储引擎会在节点间 ...

  10. 【转】Kotlin 和 Checked Exception

    Kotlin 和 Checked Exception 最近 JetBrains 的 Kotlin 语言忽然成了热门话题.国内小编们传言说,Kotlin 取代了 Java,成为了 Android 的“钦 ...