NRV优化

一、函数返回局部对象的拷贝的一般实现方式
比如有这么一段函数定义:

class X;
X bar()
{
    X x1;
    // 处理 x1..
    return x1;
}

在学习C++语法时,我们知道了。针对”Xbar()”这样的函数,是返回class X的一个对象的拷贝。其返回值是一个对象,比如叫做x2。在执行return时,x2通过调用拷贝构造函数,拷贝对象x1来实现其初始化。也就是说,这里会存在两个对象x1、x2。那么这种返回对象的拷贝,是怎么实现的呢?一般来说,C++编译器会将上段代码中bar的实现转换成如下的代码。


// 函数实现
void bar(X& __result)   // 加上一个额外参数
{
    // 预留x1的内存空间
    X x1;

    // 编译器产生的默认构造函数的调用,
    x1.X::X();

    // 处理 x1..

    // 编译器产生的拷贝操作
    __result.X::X(x1);

    return;
}

// 函数调用
X x2;                   // 这里只是预留内存,并未调用初始化函数
bar(x2);

通过上述代码,我们可见编译器对于返回对象拷贝的处理方式。
1、函数添加一个额外参数,为返回对象的引用;

2、函数调用前,先申请欲返回对象x2的内存空间;

3、将对象x2的引用传入函数中,并在函数返回前,调用x2的拷贝构造函数。

X x2 = bar();
被替换成了

X x2;  
bar(x2); 

二、NRV(Named Return Value)优化
上面的实现中,存在着x1、x2两个对象,而x1的生命周期转瞬即逝。而且对于bar()的调用者来说,根本就没有x1这个对象,调用者想要的只有x2。这样的实现能不能够将其优化变得更快呢。编译器有一种优化方式,直接将x2替代x1。编译器转换后的伪代码如下。

void bar(X& __result)
{
    // 调用__result的默认构造函数
    __result.X::X();

    // 处理__result
    return;
}

从代码看出,这里只有一个对象,也就是传入的x2。NRV优化后的实现,比原来的实现省去了如下操作:

1) 在堆栈中预留x1的内存;

2) 调用X的默认构造函数,构造x1

3) 调用X的拷贝构造函数,构造x2

4) 调用x1的析构函数

5) 堆栈中回收x1的内存

但是多了一个操作,就是调用X的默认构造函数,构造x2。对于函数的调用者(只关心x2不关心x1)来说,只有一个区别,就是x2的构造方式由调用拷贝构造函数,转变成了调用默认构造函数。


三、NRV优化触发的疑问

上面的内容在《深度探索C++对象模型》中都有详细的讲解。此外书中还提到了程序员必须给class X定义拷贝构造函数才能触发NRV优化,不然还是按照最初的较慢的方式执行。可是为什么一定要定义拷贝构造函数才能触发NRV优化呢。在网上找了半天一直没有确定的答案。于是我做了如下实验。有如下代码:

class CTest
{
public:
    CTest()
    {
        cout << "CTest()" << this << endl;
    }
    CTest(const CTest& rcTest)
    {
        cout << "CTest(CTest)" << this << endl;
    }
    ~CTest()
    {
        cout << "~CTest()" << this << endl;
    }
private:
    int a;
};

CTest foo()
{
    CTest oTestInFoo;
    return oTestInFoo;
}

int main()
{
    CTest oTest = foo();
    return 0;
}

1:原博客
2:《Inside C++ Object 》

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值