IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> C++返回值拷贝以及std::move -> 正文阅读

[C++知识库]C++返回值拷贝以及std::move

最近在看《effective modern C++》,到条款二十五,看到了C++返回值拷贝的说明,故做一下总结。并参考了网上的资料。深入理解C++中的RVO

概述

我将以下面的类为例子,说明C++在RVO的情况下以及关闭RVO情况下,其函数返回值是如何返回给调用者的,并给出其汇编代码的说明。环境是Ubuntu Linux,编译器为GCC7.5.0。

class Obj {
public:
    Obj() { // 构造函数
    std::cout << "in Obj() " << " " << this << std::endl;
    }
    Obj(int n) {
    std::cout << "in Obj(int) " << " " << this << std::endl;
    }
    Obj(const Obj &obj) { // 拷贝构造函数
    std::cout << "in Obj(const Obj &obj) " << &obj << " " << this << std::endl;
    }
    Obj(const Obj &&obj) { // 移动构造函数
    std::cout << "in Obj(const Obj &&obj) " << &obj << " " << this << std::endl;
    }
    Obj &operator=(const Obj &obj) { // 赋值构造函数
    std::cout << "in operator=(const Obj &obj)" << std::endl;
    return *this;
    }
    Obj &operator=(const Obj &&obj) { // 移动赋值构造函数
    std::cout << "in operator=(const Obj &&obj)" << std::endl;
    return *this;
    }
    ~Obj() { // 析构函数
    std::cout << "in ~Obj() " << this << std::endl;
    }
private:
    int n;
};

关闭RVO

编译命令加上-fno-elide-constructors表示关闭RVO 优化开关。定义函数fun1,并调用。

//test28_RVO.cpp
Obj fun1() {
  Obj obj;
  // do sth;
  return obj;
}

int main() {
    Obj a = fun1();
    return 0;
}

编译:g++ -fno-elide-constructors -g -o test28_RVO test28_RVO.cpp
结果打印:如下图所示。

  • 定义拷贝构造函数以及移动构造函数的情况下:
    在这里插入图片描述

  • 定义拷贝构造函数而未定义移动构造函数的情况下:
    在这里插入图片描述

结果分析:从打印可以分析出,在关闭RVO优化的情况下,C++返回一个对象会调用拷贝构造函数(或者移动构造函数)2次。分析其汇编代码,可知,在main函数调用函数fun1()的时候,调用者会将一个栈的地址传递给fun1()。在函数fun1()内,当调用构造函数创建obj之后,会将obj拷贝到这个栈的地址,作为一个临时对象。函数fun()调用结束之后,会再调用一次拷贝构造函数,临时对象拷贝到接收者即a中。
在这里插入图片描述

在RVO下

代码和上面的相同。

//test28_RVO.cpp
Obj fun1() {
  Obj obj;
  // do sth;
  return obj;
}

int main() {
    Obj a = fun1();
    return 0;
}

编译:g++ -g -o test28_RVO test28_RVO.cpp
结果打印:如下图所示。
在这里插入图片描述

结果分析:在启用RVO优化的情况下,只调用了一次构造函数,拷贝构造函数没有被调用。
从汇编代码结果可以看出,main函数将地址-0x1c(%rbp)传递给函数fun1(),之后,fun1()在调用Obj构造函数的时候,直接在这个地址上进行构造,免去了临时对象的创建以及拷贝。
在这里插入图片描述

RVO的条件

《modern effective c++》中指出,在开启RVO之后,返回局部对象只有在满足以下两个条件的情况下,编译器才会进行RVO优化。
(1)局部对象与函数返回值的类型相同。
(2)局部对象就是要返回的东西。函数形参不满足要求。
注意:当函数不同控制路径返回不同局部变量时,不会进行拷贝消除的操作,因为其不知道要返回哪个对象。

返回值为std::move的情况

代码如下:

Obj fun2() {
  Obj obj;
  // do sth;
  return std::move(obj);
}

int main() {
    Obj a = fun2();
    return 0;
}

编译:g++ -g -o test28_RVO test28_RVO.cpp
结果打印:如下图所示

  • 定义拷贝构造函数以及移动构造函数的情况下:
    在这里插入图片描述

  • 定义拷贝构造函数,未移动构造函数的情况下(之所以编译器未报错,是因为const修饰的左值形参能够接收一个右值实参):
    在这里插入图片描述

结果分析:从汇编代码中可以看出,main将变量a的地址传递给fun2()fun2()创建Obj obj,然后调用移动构造函数将obj移动到a中。
在这里插入图片描述

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-08-19 18:45:50  更:2022-08-19 18:50:03 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 13:41:46-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码