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

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. mysql workbench图形化mysql管理工具

    MYSQL官网也推出了针对Linux的图形化的连接工具-MySQL Workbench.MySQL Workbench不仅仅是一个简单的MySQL客户端.简而言之,Workbench是一个跨平台的 ( ...

  2. 高密度WIFI部署要点

    1. 划分AP组,分组带宽控制 根据区域的人数密集程度划分不同的AP组,并进行优化策略调整,分组分权限进行带宽控制,以确保单用户的2.4G带宽不低于1M,5G用户不低于2M2. 相邻AP错开信道 超高 ...

  3. 使用PHP打造QQ空间神奇图片

    说明 你一定在qq空间遇到过这样的东西:打开一张图片,上面有你的QQ号和昵称,你觉得很神奇,是不是? 其实原理很简单,那张图片是动态生成的,上面显示的信息是根据你访问的Url获得的,然后用程序动态的画 ...

  4. swift类型转换之Could not cast value of type xxx to xxx错误的一种特殊情况记录

    之前swift项目打包成Framework静态库,提供给OC项目套入使用时,有时会抱这样一个错误: 这个错误发生的概率比较随机,有时会,有时不会,而且这句话在swift中的使用,是做model类型强制 ...

  5. C语言中的随意跳转

    C语言中有一个很不常用的头文件:setjmp.h. 这个头文件是C语言底层实现的,不像math.h里面的函数都是纯C语言实现的. setjmp.h包含两个函数: longjmp 跳转到某个位置 set ...

  6. [Spring学习笔记 6 ] Spring JDBC 详解

    项目使用maven管理,pom.xml和项目组织如下: <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xs ...

  7. 解析form表单数据

    //解析form表单数据 function parseFormData(params) { var args = new Object(); for(var key in params){ if(!p ...

  8. iOS-获取当前View所在的控制器

    用一个分类,具体: .h #import <UIKit/UIKit.h> @interface UIView (CurrentController) /** 获取当前View的控制器对象 ...

  9. Eclipse SQLExplorer插件的安装和使用

    from: http://blog.csdn.net/flashlm/archive/2007/06/30/1672836.aspx 插件名称: SQLExplorer 插件分类: SQL Edito ...

  10. xtrabackup-增量备份

    增量备份之所以能工作是因为每个innodb的page都包含日志序列号(LSN).LSN是整个数据库的版本号. 增量备份会拷贝那些LSN比备份开始时新的页.有两种算法用来计算查找这些页:第一种,支持所有 ...