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()
{
}
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 + ];
a_r.copy_str(temp_str);
temp_str[size] = ;
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. C++ template —— 表达式模板(十)

    表达式模板解决的问题是:对于一个数值数组类,它需要为基于整个数组对象的数值操作提供支持,如对数组求和或放大: Array<), y(); ... x = 1.2 * x + x * y; 对效率 ...

  2. MShadow中的表达式模板

    表达式模板是Eigen.GSL和boost.uBLAS等高性能C++矩阵库的核心技术.本文基于MXNet给出的教程文档来阐述MXNet所依赖的高性能矩阵库MShadow背后的原理. 编写高效的机器学习 ...

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

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

  4. Atitit.java expression fsm 表达式词法分析引擎 v2 qaa.docx

    Atitit.java expression fsm 表达式词法分析引擎 v2 qaa.docx C:\0workspace\AtiPlatf_cms\src\com\attilax\fsm\Java ...

  5. Atitit.java expression fsm 表达式分词fsm引擎

    Atitit.java expression fsm 表达式分词fsm引擎 C:\0workspace\AtiPlatf_cms\src\com\attilax\fsm\JavaExpFsm.java ...

  6. IE css expression(表达式)

    很多时候我们需要对IE6的bug写一些hack,如max-height,absolute元素高度100%等. css里面的 expression(表达式)和js里面的差不多,如: 获取当前元素的高度: ...

  7. art template前端模板引擎

    偶然看到后台有一段代码 采用的是art template的模板引擎 地址为 http://aui.github.io/artTemplate/ 这段代码很简洁 var html = template( ...

  8. Template Method 模板设计模式

    什么是模板设计模式 对于不了解的模板设计模式的来说,可以认为如同古代的造纸术一样,纸所以成型,取决于用了模板的形状,形状又由镂空的木板组成,而你想要造什么纸,又取决于你使用什么材料. 上面提到了两个关 ...

  9. Atitit.eclipse comment  template注释模板

    Atitit.eclipse comment  template注释模板 1. Code templet1 1.1. Settpath1 1.2. 设置存储1 1.3. 导出设置1 2. Java d ...

随机推荐

  1. 网络问卷调查js实现代码

    昨天一个同行妹纸写了一个网络问卷调查的效果,但是有bug,于是就来问我该如何解决这个bug.经过我的分析,bug主要还是出在复选框的那部分,经过修改,bug问题解决,现在贴出如下代码,仅供大家参考: ...

  2. >hibernate-session中的方法

    1.操作实体对象的方法 save()  保存 update() 更新 saveOrUpdate() 保存或更新 delete() 删除 2.操作缓存的方法 clear()  清除所有缓存 evit() ...

  3. c#控制打印机杂项

    因项目中需要用到控制打印机的相关信息,此贴将网络寻找的资料做了些整理 1. C# 如何设置系统的默认打印机 using System.Runtime.InteropServices;   [DllIm ...

  4. StreamingAssets文件夹在不同平台上的引用

    On a desktop computer (Mac OS or Windows) the location of the files can be obtained with the followi ...

  5. 基于Picture Library创建的图片文档库中的上传多个文件功能(upload multiple files)报错怎么解决?

    复现过程 首先,我创建了一个基于Picture Library的图片文档库,名字是 Pic Lib 创建完毕后,我点击它的Upload 下拉菜单,点击Upload Picture按钮 在弹出的对话框中 ...

  6. Lesson 1 A private conversation

    Text Last week I went to the theatre. I had a very good seat. The play was very intersting. I did no ...

  7. Some warning were found during validation

    前几天做一个iOS下的App更新,到上传的时候出了问题,一直传了大半个小时,结果还是没传完,再试依然不行,于是只好关机,把电脑带回家弄. 回家后出现了更奇怪的事,经过漫长等待后,竟然出现这个提示: 我 ...

  8. android-plugmgr源代码分析

    android-plugmgr是一个Android插件加载框架,它最大的特点就是对插件不需要进行任何约束.关于这个类库的介绍见作者博客,市面上也有一些插件加载框架,但是感觉没有这个好.在这篇文章中,我 ...

  9. kpvalidate开辟验证组件,通用Java Web请求服务器端数据验证组件

    小菜利用工作之余编写了一款Java小插件,主要是用来验证Web请求的数据,是在服务器端进行验证,不是简单的浏览器端验证. 小菜编写的仅仅是一款非常初级的组件而已,但小菜为它写了详细的说明文档. 简单介 ...

  10. jQuery编程的最佳实践

    好像是feedly订阅里看到的文章,读完后觉得非常不错,译之备用,多看受益. 加载jQuery 1.坚持使用CDN来加载jQuery,这种别人服务器免费帮你托管文件的便宜干嘛不占呢.点击查看使用CDN ...