前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >关于C++函数返回值的拷贝优化问题

关于C++函数返回值的拷贝优化问题

原创
作者头像
卡尔曼和玻尔兹曼谁曼
修改2023-09-08 09:49:29
4750
修改2023-09-08 09:49:29
举报
文章被收录于专栏:给永远比拿愉快

在传统C++程序中,如果函数的返回值是一个对象的话,可能需要对函数中的局部对象进行拷贝。如果该对象很大的话,则程序的效率会降低。

在C++ 11以后,出现的移动语义(Move Semantic)及拷贝优化(Copy Elision)都是解决这个问题的方法。

本文试图以一个最简单的例子来说明这个问题。

案例

下面来看一个简单的例子(这里的BigObj类的实例假设是一个需要很大存储空间的大对象):

代码语言:txt
复制
#include <iostream>

using std::cout;
using std::endl;

class BigObj
{
public:
    BigObj()
    {
        cout << "这是默认构造函数" << endl;
    }

    BigObj(const BigObj& that)
    {
        cout << "这是拷贝构造函数" << endl;
    }

    BigObj(BigObj&& that)
    {
        cout << "这是移动构造函数" << endl;
    }

    
    ~BigObj()
    {
        cout << "这是析构函数" << endl;
    }
};

BigObj fun()
{
    BigObj obj = BigObj();
    return obj;
}

int main()
{
    BigObj obj = fun();
    return EXIT_SUCCESS;
}

拷贝优化

运行该程序,我们会得到如下输出:

代码语言:txt
复制
这是默认构造函数
这是析构函数

可以发现fun()函数在返回BigObj对象的时候没有进行拷贝,这是由于编译期帮我们做了拷贝优化。

移动语义

但是编译器堆函数返回值的拷贝优化并不是能完全实现的,有一些特殊情况下会失效。所以比较保险的做法是定义移动构造函数,当没有拷贝优化的时候可以通过移动语义避免低效的拷贝。

我们可以通过-fno-elide-constructors关闭编译器的拷贝优化,下面是对应的cmake文件:

代码语言:txt
复制
cmake_minimum_required(VERSION 3.26)
project(CxxTutorial)

set(CMAKE_CXX_STANDARD 23)
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-elide-constructors")

add_executable(CxxTutorial main.cpp)

通过配置关闭拷贝优化以后,我们执行上面的程序,输出结果如下:

代码语言:txt
复制
这是默认构造函数

这是移动构造函数

这是析构函数

这是析构函数

可以看到关闭拷贝优化以后,在定义了移动构造函数的时候,函数返回零时对象的时候会调用移动构造函数,转义所有权,减少数据拷贝。但是移动构造也会生成一个新的对象,所以输出结果中会调用两次析构函数,第一次析构函数是析构了函数中定义的零时对象,第二次是析构了函数返回值返回后的对象。

那如果我们没有定义移动构造函数,而且编译期也没有进行拷贝优化程序的运行会是怎么样的呢?

注释掉上面的移动构造函数,我们可以看到输出结果如下:

代码语言:txt
复制
这是默认构造函数
这是拷贝构造函数
这是析构函数
这是析构函数

这个结果是在预料之中的,没有拷贝优化,没有移动构造函数的情况下,程序会调用拷贝构造函数。假设这个对象是一个大对象,则拷贝过程会花费一些时间,降低了程序的执行效率。而使用移动语义的话,直接转义对象的所有权,效率会高一些。

结论

对于C++函数返回一个大对象的时候,在编译器能进行拷贝优化的时候,会优先进行返回值的拷贝优化。如果不能进行拷贝优化,在有定义移动构造函数的时候,则会调用移动构造函数进行返回值对象所有权转义,减少不必要的拷贝。最后,这两种情况失效的时候,才会调用拷贝构造函数进行对象的深拷贝。

有了上述结论,我们在写程序的时候最佳实践是函数返回值可以直接返回函数体内定义的零时对象,但是我们需要在定义该对象的时候实现移动构造函数。这样就可以保证函数的返回值要么有编译器拷贝优化,要么会调用移动构造函数减少拷贝开销。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 案例
  • 拷贝优化
  • 结论
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档