泛型编程-转移构造函数(Generic Programming: Move Constructor)

Only七七Hi

Only七七Hi

2016-01-29 12:21

泛型编程-转移构造函数(Generic Programming: Move Constructor),泛型编程-转移构造函数(Generic Programming: Move Constructor)
泛型编程-转移构造函数(Generic Programming: Move Constructor)
作者:Andrei Alexandrescu

提交者:eastvc 发布日期:2003-9-20 10:07:17
原文出处:http://www.cuj.com/experts/2102/alexandr.htm


编译:死猫
校对:Wang Tianxing

1 引言

我相信大家很了解,创建、复制和销毁临时对象是C++编译器最爱的户内运动。不幸的是,这些行为会降低C++程序的性能。确实,临时对象通常被视为C++程序低效的第一因素[1]。

下面的代码是正确的:

vector < string  ReadFile();vector < string  vec = ReadFile();

或者

string s1, s2, s3;//...s1 = s2 + s3;

但是,如果关心效率,则需要限制类似代码的使用。ReadFile()和operator+创建的临时对象分别被复制然后再废弃。这是一种浪费!

为了解决这个问题,需要一些不太优雅的约定。例如,可以按照引用传递函数参数:

(本文来源于图老师网站,更多请访问http://m.tulaoshi.com/cyuyanjiaocheng/)
void ReadFile(vector < string  & dest);vector < string  dest;ReadFile(dest);

这相当令人讨厌。更糟的是,运算符没有这个选择,所以如果想高效的处理大对象,程序员必须限制创建临时对象的运算符的使用:

string s1, s2, s3;//...s1 = s2;s1 += s3;

这种难缠的手法通常减缓了设计大程序的大团队的工作效率,这种强加的持续不断的烦恼扼杀了编写代码的乐趣而且增加了代码数量。难道从函数返回值,使用运算符传递临时对象,这样做是错误的吗?

一个正式的基于语言的解决方案的提议已经递交给了标准化委员会[2]。Usenet上早已引发了大讨论,本文也因此在其中被反复讨论过了。

本文展示了如何解决C++存在的不必要的复制问题的方法。没有百分之百让人满意地解决方案,但是一个干净的程度是可以达到的。让我们一步一步的来创建一个强有力的框架,来帮助我们从程序中消除不需要的临时对象的复制。这个解决方案不是百分之百透明的,但是它消除了所有的不需要的复制,而且封装后足以提供一个可靠的替代品,直到多年以后,一个干净的、基于语言的标准化的实现出现。

(本文来源于图老师网站,更多请访问http://m.tulaoshi.com/cyuyanjiaocheng/)2 临时对象和“转移构造函数”(Move Constructor)

在和临时对象斗争了一段时间之后,我们意识到在大多数情况下,完全消除临时对象是不切实际的。大多数时候,关键是消除临时对象的复制而不是临时对象本身。下面详细的讨论一下这个问题。

大多数具有昂贵的复制开销的数据结构将它们的数据以指针或者句柄的形式储存。典型的例子包括,字符串(String)类型储存大小(size)和字符指针(char*),矩阵(Matrix)类型储存一组整数维数和数据存储区指针(double*),文件(File)类型储存一个文件句柄(handle)。

如你所见,复制字符串、矩阵或者文件的开销不是来自于复制实际的数据成员,而是来自于指针或者句柄指向的数据的复制。

因此,对于消除复制的目的来说,检测临时对象是一个好方法。残酷点说就是,既然一个对象死定了,我们完全可以趁着它还新鲜,把它用作器官捐献者。

顺便说一下什么是临时对象?这里给出一个非正式的定义:

当且仅当离开一段上下文(context)时在对象上执行的仅有的操作是析构函数时,一个对象被看成是临时的。这里上下文可能是一个表达式,也可能是一个语句范围,例如函数体。

C++标准没有定义临时对象,但是它假定临时对象是匿名的,例如函数的返回值。按照我们的更一般化的定义,在函数中定义的命名的栈分配的变量也是临时的。稍后为了便于讨论我们使用这个一般化的定义。

考虑这个String类的实现(仅作为示例):

class String{char* data_;size_t length_;public:~String(){delete[] data_;}String(const String& rhs): data_(new char[rhs.length_]), length_(rhs.length_){std::copy(rhs.data_, rhs.data_ + length_, data_);}String& operator=(const String&);//...};

这里复制的成本主要由data_的复制组成,也就是分配新的内存并复制。如果可以探测到rhs实际上是临时的就好了。考虑下面的C++伪代码:

class String{//...同前...String(temporary String& rhs): data_(rhs.data_), length_(rhs.length_){//复位源字符串使它可以被销毁//因为临时对象的析构函数仍然要执行rhs.data_ =0;}//...}

这个我们虚构的重载构造函数String(temporary String&)在创建一个String临时对象(按照前面的定义)时调用。然后,这个构造函数执行了一个rhs对象转移的构造过程,只是简单的复制指针而不是复制指针指向的内存块。最后,“转移构造函数”复位源指针rhs.data_(恢复为空指针)。使用这个方法,当临时对象被销毁时,delete[]会无害的应用在空指针上[译注:C++保证删除空指针是安全的]

一个重要的细节是“转移构造”后rhs.length_没有被清0。按照教条主义的观点,这是不正确的,因为data_==0而len

展开更多 50%)
分享

猜你喜欢

泛型编程-转移构造函数(Generic Programming: Move Constructor)

C语言教程 C语言函数
泛型编程-转移构造函数(Generic Programming: Move Constructor)

Java泛型编程快速入门

编程语言 网络编程
Java泛型编程快速入门

s8lol主宰符文怎么配

英雄联盟 网络游戏
s8lol主宰符文怎么配

泛型编程与设计新思维

C语言教程 C语言函数
泛型编程与设计新思维

泛型编程:再现Min和Max

C语言教程 C语言函数
泛型编程:再现Min和Max

lol偷钱流符文搭配推荐

英雄联盟 网络游戏
lol偷钱流符文搭配推荐

STL泛型编程与设计新思维

编程语言 网络编程
STL泛型编程与设计新思维

Visual C# 2.0泛型编程基础

编程语言 网络编程
Visual C# 2.0泛型编程基础

lolAD刺客新符文搭配推荐

英雄联盟
lolAD刺客新符文搭配推荐

析构函数的奥秘

析构函数的奥秘

基本配色——可靠

基本配色——可靠
下拉加载更多内容 ↓