GCC编译器下NRV优化

阅读侯捷老师翻译的深度探索C++对象模型时所进行的相关测试以及自己的理解


一、浅讲NRV优化

NRV,即Named Result Value,指的是编译器对于我们所写代码的一种优化,如:

B pplus()
{
    B f;
    return f;
}

我们知道在一些没有NRV优化的编译器中,对于上述的代码在执行时,离开函数作用域时,会调用函数作用域内f的析构函数,也就是说我们在主函数内假定有代码B d=pplus();此时主函数结束时,总共会有两次B的析构函数(假定主函数中B的对象只有d)。而NRV优化会帮助我们在编译器层面上进行优化。下面是主要的测试内容以及分析(均在GCC环境下)

二、测试代码

#include<iostream>
using namespace std;
class A {
    public:
    int x;
    int y;
    public:
    A(int x,int y):x(x),y(y){cout<<"direct construct"<<endl;}
    A(const A&a){this->x=a.x;this->y=a.y;cout<<"copy construct"<<endl;}
    ~A(){cout<<"delete"<<endl;}
};
A pplus()
{
    A f(1,1);
    return f;
}
int main()
{
    A aa=pplus();
    return 0;
}

三、运行结果

在这里插入图片描述

四、结果分析以及解释

NRV优化对于上述代码中,所执行的操作相当于

void pplus(A& _result)
{
	// 调用__result相应的构造函数
	__result.A::A(1,1);
 
	// 处理__result
	return;

}

调用时,相当于执行:

A aa;//注意此处只是一个伪代码,并不调用真正的构造函数。
pplus(aa);

五、相关思考

在书中我了解到NRV优化必须要有拷贝构造函数的存在(不管是隐式还是显示),因此下面进行相关试验。
当把拷贝构造函数直接删去时,运行结果并没有发生变化。因此我们 可以知道隐式的拷贝构造函数(不管是bitwise copy还是合成的copy constructor)依然会触发NRV优化。
当把拷贝构造函数置为删除时,即:

A(const A&a)=delete;

此时,会报错:
在这里插入图片描述
也就是说触发NRV优化的关键是要有拷贝构造函数,不管是显示定义,还是隐式的,但是不能定义为删除的。

六、没有NRV优化时的情况

以vs自带的编译器为例,我们运行一开始的代码,会得到如下结果:
在这里插入图片描述
可以看到和GCC的编译器是有差别的,此时显然是没有NRV优化的,后来了解到一般编译器在执行形如下面的代码时,会进行一些相应的转化。

B pplus()
{
    B f;
    return f;
}

此时编译器会做出相应的转化:

void B pplus(B& _result)
{
	B f;//预留f的内存空间,此时不会执行构造函数
	f.B::B();//调用默认构造函数
	_result.B(f);
	return;
}

函数调用时(B b=pplus();),编译器会做如下处理:

B b;// 这里只是预留内存,并未调用初始化函数
pplus(b);

所以结合上述的理论,我们可以看到调用了构造函数,拷贝构造函数,2次析构函数。那么我们也可以得到在该种编译器中,拷贝构造函数也是不可或缺的(不管是显式的,还是隐式的),将拷贝构造函数定义为删除的,会引发程序的报错

总结

从上述的分析中,我们可以看到对于以下:

B pplus()
{
    B f;
    return f;
}

这种返回对象的函数来说,拷贝构造函数(无论是显式的还是隐式的)是不可或缺的,不可以把拷贝构造函数定义为删除的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值