现代C++实战30讲
吴咏炜
前 Intel 资深软件架构师
立即订阅
3681 人已学习
课程目录
已更新 14 讲 / 共 30 讲
0/4登录后,你可以任选4讲全文学习。
课前必读 (2讲)
开篇词 | C++这么难,为什么我们还要用C++?
免费
课前必读 | 有关术语发音及环境要求
基础篇 (9讲)
01 | 堆、栈、RAII:C++里该如何管理资源?
02 | 自己动手,实现C++的智能指针
03 | 右值和移动究竟解决了什么问题?
04 | 容器汇编 I:比较简单的若干容器
05 | 容器汇编 II:需要函数对象的容器
06 | 异常:用还是不用,这是个问题
07 | 迭代器和好用的新for循环
08 | 易用性改进 I:自动类型推断和初始化
09 | 易用性改进 II:字面量、静态断言和成员函数说明符
提高篇 (3讲)
10 | 到底应不应该返回对象?
11 | Unicode:进入多文字支持的世界
12 | 编译期多态:泛型编程和模板入门
现代C++实战30讲
登录|注册

10 | 到底应不应该返回对象?

吴咏炜 2019-12-18
你好,我是吴咏炜。
前几讲里我们已经约略地提到了返回对象的问题,本讲里我们进一步展开这个话题,把返回对象这个问题讲深讲透。

F.20

《C++ 核心指南》的 F.20 这一条款是这么说的 [1]
F.20: For “out” output values, prefer return values to output parameters
翻译一下:
在函数输出数值时,尽量使用返回值而非输出参数
这条可能会让一些 C++ 老手感到惊讶——在 C++11 之前的实践里,我们完全是采用相反的做法的啊!
在解释 F.20 之前,我们先来看看我们之前的做法。

调用者负责管理内存,接口负责生成

一种常见的做法是,接口的调用者负责分配一个对象所需的内存并负责其生命周期,接口负责生成或修改该对象。这种做法意味着对象可以默认构造(甚至只是一个结构),代码一般使用错误码而非异常。
示例代码如下:
MyObj obj;
ec = initialize(&obj);
这种做法和 C 是兼容的,很多程序员出于惯性也沿用了 C 的这种做法。一种略为 C++ 点的做法是使用引用代替指针,这样在上面的示例中就不需要使用 & 运算符了;但这样只是语法略有区别,本质完全相同。如果对象有合理的析构函数的话,那这种做法的主要问题是啰嗦、难于组合。你需要写更多的代码行,使用更多的中间变量,也就更容易犯错误。
取消
完成
0/1000字
划线
笔记
复制
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
该试读文章来自付费专栏《现代C++实战30讲》,如需阅读全部文章,
请订阅文章所属专栏。
立即订阅
登录 后留言

精选留言(9)

  • 木瓜777
    项目中一直使用您说的老方法,目前看编译器有优化的话,后面会逐步考虑采用返回对象的方法! 有个问题问下,如果要返回空对象,该如何做? 是直接采用空的构造函数?

    作者回复: 用默认构造函数代表空,或者用 optional<对象> (不构造)代表空,或者抛异常代表不正常(视是否不正常而定)。

    optional 会在第 22 讲里讨论。

    2019-12-18
    3
  • 小一日一
    我认为老师应该讲一下NRVO/RVO与std::move()的区别,这个问题曾经困扰过我,从stackoverflow的问题来看,学习c++11时大多数人都思考过这个问题:https://stackoverflow.com/questions/4986673/c11-rvalues-and-move-semantics-confusion-return-statement

    作者回复: 简单来说,在对本地变量进行返回时,不用 std::move。实际上,我在第 3 讲就写了:

    “有一种常见的 C++ 编程错误,是在函数里返回一个本地对象的引用。由于在函数结束时本地对象即被销毁,返回一个指向本地对象的引用属于未定义行为。理论上来说,程序出任何奇怪的行为都是正常的。

    “在 C++11 之前,返回一个本地对象意味着这个对象会被拷贝,除非编译器发现可以做返回值优化(named return value optimization,或 NRVO),能把对象直接构造到调用者的栈上。从 C++11 开始,返回值优化仍可以发生,但在没有返回值优化的情况下,编译器将试图把本地对象移动出去,而不是拷贝出去。这一行为不需要程序员手工用 `std::move` 进行干预——使用`std::move` 对于移动行为没有帮助,反而会影响返回值优化。”

    2019-12-18
    1
    2
  • nelson
    文稿中的代码片段
    ec = multiply(&temp, a, b);
    if (result != SUCCESS)
    {
      goto end;
    }

    result 应该是 ec吧

    作者回复: 多谢。已修正。

    2019-12-19
    1
  • hello world
    请问老师这个C++20什么时候发布编译器之类的啊?还是说已经有了?

    作者回复: 看这个页面吧:

    https://en.cppreference.com/w/cpp/compiler_support

    目前 GCC 领先一些(可以用 -std=c++2a 启用 20 的功能),但还没有哪家完整支持 C++20。

    2019-12-18
    2
    1
  • 光城~兴
    加入了move assignment后,默认是调用move assignment而不是copy assignment。
    2019-12-22
  • 光城~兴
    您好,老师,我想问一下c++xx与gcc版本的对应关系,还有这节提到的返回值优化在c++17中的结果与c++14及之前的结果(禁用返回值优化,编译后的结果)是不一样的,像这种有没有参考资料呢?

    作者回复: 对于功能和版本的关系,这个页面比较全:

    https://en.cppreference.com/w/cpp/compiler_support

    2019-12-22
  • 花晨少年
    我们继续变形一下:
    #include <stdlib.h>
    A getA_duang()
    {
      A a1;
      A a2;
      if (rand() > 42) {
        return a1;
      } else {
        return a2;
      }
    }
    int main()
    {
      auto a = getA_duang();
    }
    这回所有的编译器都被难倒了,输出是:
    Create A
    Create A
    Move A
    Destroy A
    Destroy A
    Destroy A

    ———————
    老师这个结果应该还是会有优化在的吧?如果完全没有优化应该是两个移动才对,a1或者a2移动给返回值是一次,返回值移动给a又是一次,如果真是这样,哪次被优化掉了?第二次吗

    作者回复: C++编译器哪会做这么不必要的事……就是一次移动。如果有返回值优化的话,一次移动都不会有。

    2019-12-21
    1
  • 花晨少年
    关于返回值优化的实验我们就做到这里。下一步,我们试验一下把移动构造函数删除:
      A(A&&) = delete;
    我们可以立即看到“Copy A”出现在了结果输出中,说明目前结果变成拷贝构造了
    ————————————————————————
    请问这种情况说的是针对getA_duang()函数吧?不包括 getA_named() 等函数吧

    作者回复: 对,是调用移动构造变成调用拷贝构造,如果原来就直接返回值优化掉了,那不会变化。

    2019-12-21
  • 西钾钾
    以下的代码中,无论是将拷贝构造函数还是移动构造函数置为delete,都不能正常编译(vs2017)。为啥只是使用一次构造函数,老师能简单讲下这个原理么?
    #include <iostream>

    using namespace std;

    // Can copy and move
    class A {
    public:
      A() { cout << "Create A\n"; }
      ~A() { cout << "Destroy A\n"; }
      A(const A&) { cout << "Copy A\n"; }
      A(A&&) { cout << "Move A\n"; }
    };

    A getA_unnamed()
    {
      return A();
    }

    int main()
    {
      auto a = getA_unnamed();
    }

    作者回复: VS 2017 下也能过的。你没有按环境要求里说的加上 /std:c++17。了解细节,可以看参考资料 [3]。

    2019-12-20
收起评论
9
返回
顶部