6.3 临时性对象(Temporary Objects)

如果我们有一个函数,以及两个T object,a和b:

T operator+(const T&,const T&);

a+b;

那么可能导致一个临时对象,以放置传回的对象。是否导致一个临时对象,视编译器的进取性(aggressiveness)以及上述操作发生的程序语境(program context)而定。例如:

T a,b;
T c = a+b;

1.编译器可能产生一个临时对象,放置a+b的结果,然后再使用T的copy constructor,把该临时性对象当做c的初始值。2.然而比较有可能的做法直接拷贝构造,将a+b放到c中,于是就不需要临时对象,以及对其constructor和destructor的调用了。

3.此外,视operator+()的定义而定,NRV优化会被实施,这将导致直接在上述c对象中求表达式的结果,避免执行copy constructor和具名(name object)对象的destructor。

三种方式获得的c对象,结果都一样。期间差异在于初始化的成本。但是编译器没有保证哪种做法,C++ Standard允许编译器对于临时对象产生的完全自由度。理论上,C++ Standard允许编译器厂商有完全的自由度,但实际上,由于市场的竞争,几乎保证任何表达式如下有这种形式,那么实现时根本不产生临时性对象:

T c = a+b;

//其加法运算符被定义
T operator+(const T&,const T&);
//或是这样
T T::operator+(const T&);

然而assignment语句,不能忽略临时对象:

c = a + b;

//C++ pseudo code
//T temp a + b;
T temp;
temp.operator(a+b); //(1)
//c = temp
c.operator = (temp);//(2)
temp.T::~T();

所以T c = a+b;总是比c = a+b;更加有效率。

第三种形式如下:

a + b;

这时候就会产生一个临时对象以存放运算的结果。这种情况实际上的子表达式(subexpressions)中十分普遍。如下:

String s("hello"),t("world"),u("!");

//以下会产生临时对象
String v;
v = s + t + u;

//也会产生临时对象
printf("%s\n",s + t);

对于“临时对象的生命周期而言”,在 Standard C++之前,临时对象的生命并没有显示指定,如上由编译器厂商自行决定;在C++ Standard之下标准:临时性对象的摧毁,应该是对完整表达式(full-expression)求值过程中的最后一个步骤,该完整表达式造成临时对象的产生。

临时对象的生命规则有两个例外。第一个是例外发生在表达式用来初始化一个object时。例如:

book verbose;
...
String progNameVersion = 
	!verbose
	? 0
	:progName + progVersion;

其中progName和progVersion都是String objects,这时候产生一个临时对象,放置加法运算的结果。

临时对象必须根据verbose的测试结果,有条件的析构。规则下,它应该再三目运算符之后尽快被摧毁,但是progNameVersion的初始化需要调用copy constructor。这种情况下等号右边已经被析构就是错误的。所以C++ Standard 说凡是持有表达执行结果的临时性对象,应该保留到object的初始化操作完整为止。

例如以下的初始化保证失败:

const char* progNameVersion = 
	progName + progVersion;

//被编译器内部转化
String temp;
operator+(temp,progName,progVersion);
progNameVersion = temp.String::operator char*();
temp.String::~String();

此时如果临时对象被摧毁,progNameVersion指向未定义的heap。

临时性对象的生命规则的第二个例外是“当一个临时性对象被一个reference绑定时”,例如:

const String &space = "";

//被编译器内部转化
String temp;
temp.String::String("");
const String& space = temp;

很明显,如果临时对象被摧毁,那么reference就没用了。

所以规则上说:如果一个临时对象绑定于一个reference,对象将残留,直到被初始化reference生命结束,或是直到临时对象的生命周期结束——视哪一种情况先到达而定。

 

猜你喜欢

转载自blog.csdn.net/weixin_28712713/article/details/84868885
6.3