昨天,室友发给我一个程序让我运行,代码如下:
#include <iostream>
#include <string>
using namespace std;
class Trans
{
public:
Trans() : a(12) {};
Trans(int id) : a(0), i(id) {};
~Trans() {
cout << "good bye " << i << endl;
}
friend std::ostream& operator<<(ostream&, const Trans&);
friend Trans operator + (const Trans&, const Trans&);
private:
int a;
int i;
};
std::ostream& operator<<(ostream& os, const Trans& tra)
{
return os << tra.a << endl;
}
Trans operator + (const Trans& t1, const Trans& t2)
{
Trans t(3);
t.a = t1.a + t2.a;
return t;
}
int main()
{
Trans p(1), q(2);
cout << p + q;
return 0;
}
我的运行结果是这样的:
然后将执行结果截图给室友看,室友说跟它的执行结果不一样,他的执行结果在0的前面多了一个good bye 3。这里就不给截图了。
首先说明,我用的GCC,室友用的是VS。同样的程序在不同的编译器下的执行结果不一样,那么,只有一种解释:编译器在幕后给你干了一些事情。
我想到了《深度探索C++对象模型》中的NRV(Named Return Value)优化。
对于函数:
Trans operator + (const Trans& t1, const Trans& t2)
{
Trans t(3);
t.a = t1.a + t2.a;
return t;
}
编译器会将它转换为类似这样:
void operator +(Trans *this, const Trans& t1, const Trans& t2, Trans &__result)
{
Trans t(3);
t.a = t1.a + t2.a;
__result(t);
return;
}
以上的代码在变量和函数名上并没有像编译器一样进行转换,这里只关注编译器是如何返回对象的。
如果编译器采用NVR优化,代码就会被转换成类似这样:
void operator +(Trans *this, const Trans& t1, const Trans& t2, Trans &__result)
{
__result.Trans::Trans(3);
__result.a = t1.a + t2.a;
return;
}
因此,如果不采用NVR优化,在返回一个对象时,会再创建一个临时对象用于获取返回值,因此,此函数会产生两个对象,如果采用NVR优化,在返回一个对象时,直接用返回值去取代函数内部的局部对象,此时,函数只产生一个对象。所以,对于VS和GCC的不同,或许(这只是我的一种解释这种行为的想法)可以理解为VS没有采用NVR优化,所以会有两个对象析构,而GCC采用了NVR优化,所以只有一个对象被析构。
参考资料:
深度探索C++对象模型,66页,在编译器层面做优化。