1.前言

  在前一篇文章自己实现简单的string类中提到在实现+操作符重载函数时,为了防止返回时生成的临时对象调用拷贝构造函数动态申请内存空间,使用了一个叫move的函数,它是C++0x新增的特性。既然是C++0x新增的特性,那么在以前没有这个特性的情况下,对于临时对象动态申请内存空间的问题是不是可以有其它的方法解决或避免呢?答案是肯定的,可以用Expression Template(表达式模板,ET)来解决。

2.表达式模板

  对于前面的String类,我们可能经常会使用下面的表达式:

  String str4 = str1 + str2 + str3;

  这个表达式有一个问题,就是产生了“不必要”的临时对象。因为 str1 + str2 的结果会存放在一个临时对象 temp1上,然后temp1 + str3的结果会存放在令一个临时对象temp2上,temp2最后把结果传给str4 进行初始化。如果这些向量很长,或者表达式再加几节,不仅会产生很多的临时对象,而且要动态申请内存空间来存放String内部的字符串,这很明显是低效的。move函数解决了当+操作符重载函数返回临时对象时,编译器在栈中为其拷贝另一个对象的开销(由于+操作符重载函数内声明的临时对象temp1在函数的作用域外事无法使用的,所以编译器自动的在调用+操作符重载函数的域内声明了一个临时对象temp11,用以存放拷贝的temp1),但并没有解决产生临时对象的问题。表达式模板的思路很简单,就是对+操作进行推迟计算,即一次性计算所有的+操作,这样就不需要产生临时对象来保存+操作的临时结果。由于要推迟计算,所以必须保存str1,str2,str3操作数用于后面真正计算推迟的+操作。

3.基于String的表达式模板实现

  虽然表达式模板的思路很简单,但其实现确不是想象的那么简单。原来的做法中,operator + 直接进行了计算,既然我们不想它“过早”的计算,那么我们就必须重新重载一个operator + 运算符,在这个运算中不进行真正的运算,只是生成一个对象,在这个对象中把加法运算符两边的操作数保留下来,然后让它参与到下一步的计算中去。(好吧,这个对象也是临时的,但它的代价非常非常小,因为它不需要动态申请内存空间,我们先不理会它)。

class String;template <typename L>
class ExpPlus {const L &lstr;const char *rstr;const int _size;
public:ExpPlus(const L  & a_l, const char *a_r):lstr(a_l), rstr(a_r), _size(strlen(a_r)){}ExpPlus(const L  & a_l, const char &a_r):lstr(a_l), rstr(&a_r), _size(1){}ExpPlus(const L  & a_l, const String &a_r):lstr(a_l), rstr(a_r.c_str()), _size(a_r.size()){}void copy_str(char *d_str) const;int size() const{return _size + lstr.size();}
};template <typename L>
void ExpPlus<L>::copy_str(char *d_str) const
{lstr.copy_str(d_str);strncpy(d_str + lstr.size(), rstr, _size);
}
//用于+模板
template <typename L>
ExpPlus<L> operator + (const L & a_l, const char  & a_r) {return ExpPlus<L>(a_l, a_r);
}template <typename L>
ExpPlus<L> operator + (const L & a_l, const char*  a_r) {return ExpPlus<L>(a_l, a_r);
}template <typename L>
ExpPlus<L> operator + (const L & a_l, const String  & a_r) {return ExpPlus<L>(a_l, a_r);
}

  我们增加了一个模板类 ExpPlus,用它来代表加法计算的“表达式”,但在进行加法时,它本身并不进行真正的计算。对这个类,定义了copy_str函数,在这个函数中才进行了真正的字符串加法计算。当然,它除了支持保存String对象,还支持保存char和char *。因为String对象加字符串常量和字符也是很常见的,如下面的表达式:

  String str2 = str1 + 'a' + "cd"

  对于我们实现的String类,必须重新重载一个operator = 函数,用于一次性计算右边表达式的值。修改后的String代码如下:

class String
{
public:。。。template <typename Exp>String& operator=(const Exp &a_r); //新加
    。。。void copy_str(char *d_str) const; //新加
};void String::copy_str(char *d_str) const
{strcpy(d_str, _string);
}template <typename Exp>
String& String::operator=(const Exp &a_r)
{char *temp_str;int size = a_r.size();temp_str = new char[size + 1];a_r.copy_str(temp_str);temp_str[size] = 0;if (_string)delete _string;_string = temp_str;_size = size;return *this;
}

  上面只给出了新加的代码,其它代码在自己实现简单的string类中已经给出。

  上面这段话,对于不了解ET的人来说,也许一时间还不容易明白,我们一步一步来:

  在 str4 = str1 + str2 + str3这个式子中,首先遇到 str1 + str2,这时,模板函数 operator + 会被调用,这时只是生成一个临时的ExpPlus<String>对象(我们叫它 t1 吧),不做计算,只是保留计算的左右操作数(也就是str1和str2),接着,t1 + str3 ,再次调用同样的 operator + ,而且也只是生成一个对象(我们叫它 t2 吧),这个对象的类型是 ExpPlus<ExpPlus<String>>,同样,t2 在这里只是保留了两边的操作数(也就是 t1 和 str3)。直到整个表达式“做完”,没有任何东西进行了计算,所做的事情实际上只是用 ExpPlus 这个模板类把计算式的信息记录下来了(当然,这些信息就是参与计算的操作数)。

  最后,当进行 str4 = t2 的时候,String的赋值运算符被调用(用 t2 作参数)。注意,这个调用中的语句a_r.copy_str(temp_str),实际是是调用t2.copy_str(temp_str),t2.copy_str函数中又调用t1.copy_str,t1又调用str1.copy_str,就这样一步一步地将str1,str2,str3中的字符串拷贝到temp_str中,最终得到str4 = str1 + str2 + str3。就像变“魔术”一样,我们通过ExpPlus完成了“延迟计算”,并避免了大型的 String临时对象的产生。

4.总结

  表达式模板保持了表达式直观和效率两者,很强大,但很显然它太复杂,主要是作为类库的设计者的武器。另外,它也可能使得使用者要理解一些“新”东西,比如,如果我想存储表达式的中间值,那么 <ExpPlus<ExpPlus<...<String>...> 一定会让使用者理解半天。

参考:http://www.cnblogs.com/liyiwen/archive/2009/12/03/1616627.html

http://www.cppblog.com/kesalin/archive/2009/05/28/85983.html

Expression Template(表达式模板,ET)相关推荐

  1. 使用Tensor Expression张量表达式处理算子

    使用Tensor Expression张量表达式处理算子 这是TVM中Tensor表达语言的入门教程.TVM使用特定于域的张量表达式来进行有效的内核构造. 本文将演示使用张量表达式语言的基本工作流程. ...

  2. C++_member template成员模板

    C++_member template成员模板

  3. .NET平台开源项目速览(8)Expression Evaluator表达式计算组件使用

    在文章:这些.NET开源项目你知道吗?让.NET开源来得更加猛烈些吧!(第二辑)中,给大家初步介绍了一下Expression Evaluator验证组件.那里只是概述了一下,并没有对其使用和强大功能做 ...

  4. delegate、Lambda表达式、Func委托和Expression(TDelegate)表达式目录树

    1.delegate MSDN:一种安全地封装方法的类型,它与 C 和 C++ 中的函数指针类似.与 C 中的函数指针不同,委托是面向对象的.类型安全的和保险的.委托的类型由委托的名称定义 class ...

  5. 《专家系统(开发)--表达式检测--与表达式模板一起使用》

    2019独角兽企业重金招聘Python工程师标准>>> <专家系统破解篇 九.Simple Rule Engine规则推理引擎三(表达式分析)> 表达式模板化配置的思路 ...

  6. C++Postfix Expression 后缀表达式的评估算法(附完整源码)

    C++Postfix Expression 后缀表达式的评估算法 C++Postfix Expression 后缀表达式的评估算法完整源码(定义,实现,main函数测试) C++Postfix Exp ...

  7. 通过创建动态类型 动态构建Expression Select表达式来控制Property可见性

    通过创建动态类型 动态构建Expression Select表达式来控制Property可见性 项目中经常遇到的一个场景,根据当前登录用户权限,仅返回权限内可见的内容.参考了很多开源框架,更多的是在V ...

  8. Django Template 网页模板(五)

    Django Template 网页模板 6. Template 网页模板 6.1 Templeate 基础知识 6.2 Templeate 小案例 6.3 Template 标签 6. Templa ...

  9. template.js模板引擎下载和实例

    定义: art-template 是一个简约.超快的模板引擎.它采用作用域预声明的技术来优化模板渲染速度,从而获得接近 JavaScript 极限的运行性能,并且同时支持 NodeJS 和浏览器 下面 ...

最新文章

  1. netsh命令修改ip
  2. 饿了么交易系统5年演化史
  3. 【keras】rnn中的LSTM
  4. python 基础(十)
  5. perl6正则 4: before / after 代码断言: ?{} / !{}
  6. 优秀程序员和一般程序员差别在哪?
  7. UDK控制台命令概览
  8. Linux、命令ps 各字段意思
  9. struts一个action处理多个方法
  10. VS2010启动总是遇到异常提示的解决
  11. matlab在图像处理中的应用实验,MATLAB实验Matlab在数字图像处理中的应用
  12. IBM Watson:好的AI能够在方方面面推动人类文明的发展
  13. 经历了6面,终于入职蚂蚁金服
  14. 如何在WORD2007中文档中,奇数页页眉是书名,偶数页页眉是章节。各章章节不同,请详细步骤!!!...
  15. 计算机音乐恋曲1990字谱,歌曲恋曲1990简谱
  16. 微信开发者工具使用git
  17. 拔丝芋头的Java学习日记--Day4
  18. RealView MDK 使用
  19. shopee首站入驻哪个国家?哪个国家更好卖?
  20. 视觉3D感知(一):初步认识

热门文章

  1. Windows安装python,以及python的集成开发环境Pycharm
  2. java优先队列_Java高级特性增强-多线程
  3. 在sql中将表建在别的构件中用什么语句_SQL实战
  4. mysql中的删除语法错误_mysql – EXPLAIN中的SQL语法错误
  5. miniblink载入html,winform使用miniblink展示html(全屏)
  6. java swt最小化到托盘_SWT 中实现最小化到托盘图标,并只能通过托盘的弹出菜单关闭程序...
  7. Hadoop:HDFS的概念理解和体系架构-成都加米谷大数据分享
  8. 工业互联网解决方案创新应用报告(2020)
  9. 作者:钱卫宁(1976-),男,华东师范大学计算机科学与软件工程学院教授、博士生导师。...
  10. 作者:张宇中(1969-),男,中国电信股份有限公司云计算分公司首席数据分析师、大数据分析顾问。...