【转】C++中的返回值优化

2023-11-09

C++中的返回值优化

在这边文章里用到了以下编译器和操作系统,大家请自行安装

$ uname -ar
Linux debian 4.9.0-3-amd64 #1 SMP Debian 4.9.30-2+deb9u3 (2017-08-06) x86_64 GNU/Linux

$ lsb_release -a
No LSB modules are available.
Distributor ID: Debian
Description:    Debian GNU/Linux 9.1 (stretch)
Release:        9.1
Codename:       stretch

$ clang++ --version
clang version 5.0.0 (tags/RELEASE_500/final)
Target: x86_64-unknown-linux-gnu
Thread model: posix

$ g++ --version
g++ (Debian 6.3.0-18) 6.3.0 20170516
Copyright (C) 2016 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

首先我们先来看一道题,下面的代码运行之后会输出什么结果?

#include <iostream>
using namespace std;

class C {
public:
  C() { cout <<  "Constructor" << endl; }
  C(const C&) { cout << "Copy Constructor" << endl; }
  ~C() { cout << "Destructor" << endl; }
};

C foo() {
  C c1;
  return c1;
}

int main() {
    foo();
    return 0;
}
A.
Constructor
Copy Constructor
Destructor
Destructor

B.
Constructor
Destructor

我想大多数人会选A,对吗?因为foo函数在返回C类的对象时会调用拷贝构造函数来创建一个临时对象。

现在让我们编译并运行这个程序,看看输出结果是否如我们所料

$ clang++ -std=c++11 foo.cpp
$ ./a.out 
Constructor
Destructor

然而,遗憾的是,事实与课本里说的并不一样,那么,为什么会这样呢?从实际编译运行的输出来看,C类的拷贝构造函数并没有被调用。
这是因为在实际工程中大多数时候C++构造对象的开销巨大,编译器为了生成高效的代码,在foo函数返回时并没有调用拷贝构造函数去生成一个临时对象,而是直接使用在foo函数内初始化的对象c1作为返回值传出去。这就是C++中的返回值优化(Return Value Optimization, RVO)。

由于较新版本的gcc/clang均已默认开启了返回值优化(即使没有加上优化选项),我们想看到书上所说的现象只能在编译的时候加上特殊选项,让编译器不要做返回值优化,操作步骤如下:

$ clang++ -std=c++11 -fno-elide-constructors foo.cpp
$ ./a.out 
Constructor
Copy Constructor
Destructor
Destructor

返回值优化能在一定程度上提高程序的运行效率,但是我们在实际工程中最好不要依赖这个编译器优化特性,因为它要求的条件非常苛刻(或者说,编译器还不够聪明:P)。让我们在foo函数中加上一个无用的条件判断语句,试试编译器是否能继续应用返回值优化。

#include <iostream>
using namespace std;

class C {
public:
  C() { cout <<  "Constructor" << endl; }
  C(const C&) { cout << "Copy Constructor" << endl; }
  ~C() { cout << "Destructor" << endl; }
};

C foo() {
  C c1, c2;
  if (true) {
    return c1;
  } else {
    return c2;
  }
}

int main() {
    foo();
    return 0;
}

可以发现,在上面的代码中,如果编译器足够聪明,它应该能推断出foo函数只会返回c1,所以可以对foo函数应用返回值优化,那么让我们来实际编译运行一下,看看执行情况。

$ clang++ -std=c++11 -O3 foo.cpp 
$ ./a.out 
Constructor
Constructor
Copy Constructor
Destructor
Destructor
Destructor

$ g++ -std=c++11 -O3 foo.cpp 
$ ./a.out 
Constructor
Constructor
Copy Constructor
Destructor
Destructor
Destructor

可以看到,clang 5.0.0和gcc 6.3.0即使把优化选项开到最大也不能对foo函数进行返回值优化。

返回值优化还带来了一个问题,那就是拷贝构造函数和析构函数的调用变得不可预测。如果在拷贝构造函数和析构函数中存在有副作用(side-effect)的语句,就会造成不同的编译器配置编译出来的程序运行结果不一致。事实上,这是被C++11标准所允许的,下面这段话引用自C++11标准Sec 12.8 Copying and moving class objects [class.copy]

When certain criteria are met, an implementation is allowed to omit the copy/move construction of a class object, even if the copy/move constructor and/or destructor for the object have side effects.

是否调用拷贝构造函数和析构函数的决定交给了编译器实现去判断,所以在实际工程中为了写出可移植的代码,就需要避免在构造函数和析构函数中加入有副作用的语句,并且应该尽量把复杂的逻辑剥离出来,放在类的其它成员函数中实现。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【转】C++中的返回值优化 的相关文章

  • c++中string类与字符串数组

    strlen及用 给c 字符串数组赋值 strlen 很笨 它会在遇到 0之前一直找下去 所以在cstr2中没有 0的时候 它会一直找下去 而那些地方还没有被初始化过 所以就是乱的 而且strlen计算出的字符串数组长度是不包含 0的那部分
  • C++中STL库

    六个部分组成 容器 用来存放数据的各种数据结构 比如vector deque list set和map等 是一种类模板 算法 各种常用的算法 比如排序算法 拷贝算法 查找算法等 是一种函数模板 迭代器 容器与算法间的粘合剂 共有五种类型 重
  • C++中unique函数

    C 中unique函数 文章一 unique是STL中很实用的函数之一 需要 include 感谢各位提醒 下面来简单介绍一下它的作用 unique的作用是 去掉 容器中相邻元素的重复元素 这里去掉要加一个引号 为什么呢 是因为它实质上是一
  • C#:复制文件显示进度条

    1 窗口界面 主要是文本框textBox 按钮button 进度条prograssBar三大组件所组成的 2 完整代码 using System using System IO using System Windows Forms usin
  • c++11:动态内存与智能指针

    为了更容易 同时也更安全 地使用动态内存 新的标准库提供了两种智能指针 smart pointer 类型来管理动态对象 智能指针的行为类似常规指针 重要的区别是它负责自动释放所指向的对象 新标准库提供的这两种智能指针的区别在于管理底层指针的
  • C++运算符的优先级

    有括号的优先级最高 涉及对象的运算符 自增自减 取值 取地址运算符 逻辑非 按位取反 强制类型转换 长度运算符 先乘除后加减 左移右移运算符 比较运算符 三目运算符 各类赋值运算符 逗号运算符优先级最低 注意 同一优先级的运算符 运算次序由
  • C# 基础知识 (五).变量类型和字符串处理

    这篇文章是阅读 C 入门经典 Beginning C 书籍里面的内容 作者Karli Watson 主要包括自己缺乏的一些C 基础知识和在线笔记使用 文章主要包括C 简单变量类型和复杂变量类型 命名规则 隐式转换和显示转换 变量字符串处理等
  • cpp基础:对象和类

    1 类名首字母大写 Wakawaka 2 类接口 编写类的人提供的一个操作类成员的方法 3 通过关键字实现类成员的访问控制 4 封装 将实现细节和抽象分开被称为封装 如 h和 cpp文件分开 接口是实现封装的重要一环 5 默认访问权限pri
  • C++ 继承详解

    C 继承 继承语法 继承方式 private继承特点 改变访问权限 名字遮蔽 继承时的对象模型 无变量遮蔽 有变量遮蔽 final关键字 继承语法 继承的一般语法为 class 派生类名 继承方式 基类名 派生类新增加的成员 继承方式 继承
  • C++:std::thread

    1 std thread的用法 头文件为 include
  • C++学习笔记-宏(define),类型定义符(typedef),Using,内联函数(inline),指针常量和常量指针的区别与联系

    宏 define 类型定义符 typedef 内联函数 inline 的区别与联系 宏 define define 程序编译的四个阶段 条件编译 tydedef typedef和 define之间的区别 指针常量和常量指针 using 内联
  • windows下dll文件的创建详细教程

    1 前言 dll文件是啥 就不作过多赘述了 现在直接教大家如何创建与使用dll文件 本文基于windows系统 使用的编译相关工具为visual studio 2019 2 创建dll 2 1 创建dll工程 首先打开visual stud
  • 【转】C++中的返回值优化

    C 中的返回值优化 在这边文章里用到了以下编译器和操作系统 大家请自行安装 uname ar Linux debian 4 9 0 3 amd64 1 SMP Debian 4 9 30 2 deb9u3 2017 08 06 x86 64
  • C++编译时多态和运行时多态

    编译时多态 运行时多态 两种多态的优缺点 多态就是指一个基类指针根据所指对象的不同而有不同的功能和行为 编译时多态 编译时多态 又叫静态多态 早绑定 编译时多态基于template 模板 的具现化与函数的重载解析 这种多态在编译期进行 因此
  • C# 基础知识 (一).概念与思想篇

    在C 中有一些我自己认为比较独特的知识点 这些知识点是我经常使用的知识 但对它们的了解还是比较少的 所以通过查找资料学习 总结了这些独特的知识点并简单叙述 第一篇主要是一些概念和思想方面的知识 后面还有C 其他篇的文章 一 C 概念 C 语
  • C# 基础知识 (四).C#简介及托管代码

    暑假转瞬即逝 从10天的支教生活到1周的江浙沪旅游 在这个漫长的暑假中我经历了很多东西 也学到了很多东西 也认识到了很多不足之处 闲暇之余我准备重新进一步巩固C 相关知识 包括C 入门知识 C 并行开发 ASP网站等 这篇文章我介绍的是书籍
  • C#基于LINQ对数据库的增删查改

    1 增加功能 主窗口 using System using System Collections Generic using System ComponentModel using System Data using System Draw
  • 修饰符-访问修饰符internal sealed

    摘自 internal C 参考 摘自 sealed C 参考 Internal 访问仅限于当前程序集 protected internal 访问限制到当前程序集或从包含派生的类型的类别 程序集就是代码编译后bin目录下生产的 exe或者
  • C++ 生命周期

    C 程序的生命周期要经过编码 Coding 预处理 Pre processing 编译 Compiling 和运行 Running 四个阶段 编码即coding阶段 这阶段主要是定义变量 写语句 实现各种数据结构 函数和类 预处理是 C C
  • 总结c++笔试题目

    若有以下说明和语句 请选出哪个是对c数组元素的正确引用 int c 4 5 cp 5 cp c A cp 1 B cp 3 C cp 1 3 D cp 2 正确答案 D 解析 cp c 这个语句是将数组第0行的地址赋给了cp cp 1使指针

随机推荐

  • Cocos2d-x Js Binding 的手动绑定实现

    http www ityran com archives 4902 Cocos2d x Js Binding 的手动绑定实现 一叶 cocos2d x 08 13 2304 4条评论 随着 Cocos2d x 的发展 Cocos2d htm
  • Command ‘roscore‘ not found, but can be installed with: sudo apt install python-roslaunch

    ubuntu18 04安装ros melodic时报错 解决方法 查看是否安装包 cd opt ros melodic bin ls 发现没有roscore 安装 在bin目录 sudo apt get install ros melodi
  • NGINX指定启动的配置文件

    若不指定安装路径 nginx默认安装在 usr local nginx路径下 若不指定nginx的配置文件 nginx默认启动找的是同级nginx更路径下的 conf nginx conf配置文件 但该配置文件的所在路径 以及文件名不是绝对
  • git revert后无法merge发生的惨案

    git revert后无法merge发生的惨案 前景描述 目前公司开发模式是这样的 有测试分支test 开发分支dev1 dev2 dev3 dev1 dev2 dev3都是基于test分支拉出来的 各分支如果没有问题后都会合并到test
  • c# postgresql帮助类

    1 安装Npgsql 2 代码
  • 判断Java中map键值对中是否包含一个key

    boolean b map containsKey menuId
  • 前端拼音首字母搜索姓名

    前言 实际工作中碰到一个业务场景 只需要输入姓名字母简称就可以直接匹配出相应的汉字 这样可以极大的方便用户检索 提升用户的使用体验 具体需求 在搜索框中根据拼音首字母来快速搜索出人名 例如曾琴琴 曾是多音字 可以通过输入 z c zq cq
  • socket通讯相互发送读取xml实例

    首先了解下socket通讯传输数据的特点 数据在网络传输时使用的都是字节流或字符流 Socket也不例外 所以我们发送数据的时候需要转换为字节发送 读取的时候也是以字节为单位读取 那么问题就在于socket通讯时 接收方并不知道此次数据有多
  • git 某次提交补丁的生成与应用

    使用git format patch生成所需要的patch git format patch s 1bbe3c8c197a35f79bfddaba099270a2e54ea9c7 please replace the hash code w
  • 协议栈中绑定流程的一点认识

    2011年4月27日 初学者关于协议栈中绑定流程的一点认识 本人刚接触zigbee不久 在学习中将不断记录每天的进步 期待大家的指导 在这里指出我还是参考了ZStack CC2530 2 3 1 1 4 0 Projects zstack
  • Motion Detection

    Frame Difference Method FDM Background Subtraction Method BSM B is background image Adaptive Background Substraction Met
  • 在Windows上编译 CEF3 且加入mp3/mp4的支持

    现在因为工作需要 为了得到支持mp3 mp4的cef32和64位版本 需要编译cef3 本次编译版本是3239 6 0 3239 132 一 编译条件 1 可用于稳定的下载网络 2 Win7或者更新的系统 必须64位 至少8GB的RAM 我
  • Android实现网络请求方法

    Android网络请求 1 安卓开发网络请求可谓是安卓开发的灵魂 如果你不会网络请求 那么你开发的应用软件就是一具没有灵魂的枯骨 在安卓开发中进行网络请求和java中的网络请求有异曲同工之妙 但是安卓软件毕竟的安装在我们手机上的 而平常的应
  • 快递运输【Java】

    快递运输 Java 题目描述 一辆运送快递的货车 运送的快递均放在大小不等的长方体快递盒中 为了能够装载更多的快递 同时不能让货车超载 需要计算最多能装多少个快递 快递的体积不受限制 快递数最多1000个 货车载重最大50000 输入描述
  • 白山头讲PV:calibre中如何extract net

    在进行lvs分析的时候 能够对net进行trace 具有非常重要的意义 本文详细介绍如何在calibre中对一条net进行trace并highlight 如何trace trace的操作非常简单 右击你要trace的net的一部分 shap
  • opencv_python答题卡自动判卷

    opencv python答题卡自动判卷 设定答题卡模板 该图像为答题卡的答题区域 黑色边框是为了能够在各种环境中轻易的检测 左部分和上部分的黑色矩形 是为能够定位到答题选项的坐标而设置 同时题目数量为20 3共60道选择题 在进行批改试卷
  • 解释一下https://so.csdn.net/so/chat/这个网站的源码

    很抱歉 我不能直接访问网站的源代码 然而我可以告诉你一般情况下 网站的源代码是构建网站的基础 它包含了网页的 HTML CSS 和 JavaScript 代码 这些代码指导浏览器如何呈现网页的内容和样式 如果您对网站的源代码感兴趣 可以使用
  • 【Vue】中 $attrs 中的使用方法

    https www cnblogs com wuxianqiang p 10452662 html
  • p标签在div中垂直居中且文本左对齐

    父元素div设置 text align center p元素设置 display inline block vertical align middle text align left
  • 【转】C++中的返回值优化

    C 中的返回值优化 在这边文章里用到了以下编译器和操作系统 大家请自行安装 uname ar Linux debian 4 9 0 3 amd64 1 SMP Debian 4 9 30 2 deb9u3 2017 08 06 x86 64