泛型编程-转移构造函数(Generic Programming: Move Constructor)
1 引言 我相信大家很了解,创建、复制和销毁临时对象是C++编译器最爱的户内运动。不幸的是,这些行为会降低C++程序的性能。确实,临时对象通常被视为C++程序低效的第一因素[1]。 下面的代码是正确的:
或者
但是,如果关心效率,则需要限制类似代码的使用。ReadFile()和operator+创建的临时对象分别被复制然后再废弃。这是一种浪费! 为了解决这个问题,需要一些不太优雅的约定。例如,可以按照引用传递函数参数:
这相当令人讨厌。更糟的是,运算符没有这个选择,所以如果想高效的处理大对象,程序员必须限制创建临时对象的运算符的使用:
这种难缠的手法通常减缓了设计大程序的大团队的工作效率,这种强加的持续不断的烦恼扼杀了编写代码的乐趣而且增加了代码数量。难道从函数返回值,使用运算符传递临时对象,这样做是错误的吗? 一个正式的基于语言的解决方案的提议已经递交给了标准化委员会[2]。Usenet上早已引发了大讨论,本文也因此在其中被反复讨论过了。 本文展示了如何解决C++存在的不必要的复制问题的方法。没有百分之百让人满意地解决方案,但是一个干净的程度是可以达到的。让我们一步一步的来创建一个强有力的框架,来帮助我们从程序中消除不需要的临时对象的复制。这个解决方案不是百分之百透明的,但是它消除了所有的不需要的复制,而且封装后足以提供一个可靠的替代品,直到多年以后,一个干净的、基于语言的标准化的实现出现。 2 临时对象和“转移构造函数”(Move Constructor) 在和临时对象斗争了一段时间之后,我们意识到在大多数情况下,完全消除临时对象是不切实际的。大多数时候,关键是消除临时对象的复制而不是临时对象本身。下面详细的讨论一下这个问题。 大多数具有昂贵的复制开销的数据结构将它们的数据以指针或者句柄的形式储存。典型的例子包括,字符串(String)类型储存大小(size)和字符指针(char*),矩阵(Matrix)类型储存一组整数维数和数据存储区指针(double*),文件(File)类型储存一个文件句柄(handle)。 如你所见,复制字符串、矩阵或者文件的开销不是来自于复制实际的数据成员,而是来自于指针或者句柄指向的数据的复制。 因此,对于消除复制的目的来说,检测临时对象是一个好方法。残酷点说就是,既然一个对象死定了,我们完全可以趁着它还新鲜,把它用作器官捐献者。 顺便说一下什么是临时对象?这里给出一个非正式的定义: 当且仅当离开一段上下文(context)时在对象上执行的仅有的操作是析构函数时,一个对象被看成是临时的。这里上下文可能是一个表达式,也可能是一个语句范围,例如函数体。 C++标准没有定义临时对象,但是它假定临时对象是匿名的,例如函数的返回值。按照我们的更一般化的定义,在函数中定义的命名的栈分配的变量也是临时的。稍后为了便于讨论我们使用这个一般化的定义。 考虑这个String类的实现(仅作为示例):
这里复制的成本主要由data_的复制组成,也就是分配新的内存并复制。如果可以探测到rhs实际上是临时的就好了 |
凌众科技专业提供服务器租用、服务器托管、企业邮局、虚拟主机等服务,公司网站:http://www.lingzhong.cn 为了给广大客户了解更多的技术信息,本技术文章收集来源于网络,凌众科技尊重文章作者的版权,如果有涉及你的版权有必要删除你的文章,请和我们联系。以上信息与文章正文是不可分割的一部分,如果您要转载本文章,请保留以上信息,谢谢! |