【问题标题】:Memory footprint of C++ functions returning by reference or value?通过引用或值返回的 C++ 函数的内存占用?
【发布时间】:2013-06-22 21:26:36
【问题描述】:

我正在尝试减少主要使用 C++ 开发的应用程序的内存占用,而我正在研究的一件事是通过引用返回一个特别大的数据结构,而不是通过值返回它们。我需要一些确认。例如,假设我在 some_object 类中有成员函数,如下所示:

const some_type& get_some_type(...) {
   ...;
   return ...;
}

对比

const some_type get_some_type(...) {
   ...;
   return ...;
}

我的理解是否正确,通过调用后者(按值返回)进行赋值会在应用程序执行期间的某些点在内存中留下两个数据副本some_type,而调用前者(通过引用返回)可以避免这种情况?

作为减少内存占用的总体目标的一部分,另一项努力是更改some_type 的定义,以防止使用上述按引用返回选项。在这种情况下,由于some_type(它实际上是一个容器,比如data_type 类型的数据)在循环中使用如下:

const some_type& = some_object->get_some_type(); // returns by ref. a large container
for(...) {
   ...;
   data_type = some_type.at(...);
   ...;
}

我假设如果我们被限制使用上面的按值返回,那么我需要通过引入一个新函数get_some_type_at 来按值获取容器的元素(是否有任何缺点这样做是我应该注意的,比如说,在性能方面?):

for(...) {
   ...;
   data_type = some_object->get_some_type_at(...); // returns by value a small object
   ...;
}

我再次主要是在寻找对这些方面的确认。但细节和见解是最受赞赏的。感谢您的时间和兴趣!

【问题讨论】:

    标签: c++ function return-by-reference return-by-value


    【解决方案1】:

    大多数优秀的编译器都实现了返回值优化,因此您不必担心复制对象的不必要构造。

    因此,选择

    some_type get_some_type(...) {
       ...;
       return ...;
    }
    

    除非你使用的是旧的编译器。

    (感谢 juanchopanza;同时删除 const 对象返回;cmets 中的解释。)

    详情请见http://en.wikipedia.org/wiki/Return_value_optimization;在这里复制是没有意义的。

    【讨论】:

    • +1,尽管您可能不想返回 const 对象。这会扼杀移动语义。
    • 其实每次可以返回常量引用,RVO是做不到的,因为你可以返回引用,如果你将对象保存在类中,RVO只能在对象是临时的。
    • @juanchopanza;很尖。我会调整答案。
    • 不知道 - 感谢@Bathsheba 提供的有用参考。
    • 我在另一篇文章中读到,在某些情况下,即使是为了价值,也建议返回 const;例如,执行 if( func() = some_value ) 之类的事情。
    【解决方案2】:

    我的理解是否正确,通过调用后者(按值返回)进行分配会在应用程序执行期间的某些点在内存中保留两个数据 some_type 副本,而调用前者(按引用返回)避免了这种情况?

    对于现代编译器,这种可能性非常小。该标准特别祝福所谓的返回值优化(和命名的返回值优化),让编译器避免在这种情况下创建额外的副本。我所知道的每一个相当现代的编译器都包含这种优化。在某些情况下(例如 gcc),当您告诉编译器 进行优化时,它们甚至会进行此优化。启用优化后,您必须花费大约 10 年(可能更长时间)才能找到没有做到这一点的编译器。

    C++11 添加了“移动”功能,可以做更多的事情来确保解决您所关心的问题,即使您按值返回一个大对象。

    底线:在我看来,你在错误的地方寻找什么——你可能想考虑在你的类中添加一个显式的移动构造函数(可能还有移动赋值运算符),但一般来说,你可以直接返回通过价值和快乐。

    【讨论】:

    • 为了清楚起见,您是说这甚至适用于大型容器,例如,假设我有一个 std::map<:pair>,std::vector >?并说地图的大小超过1亿?在进入循环之前做我所做的事情是安全的,根据你的说法,使用现代编译器,内存不会重复?这样看来,通过引用返回似乎已经过时了。
    • @squashed.bugaboo:是的,即使是大地图也可以。返回引用并不是完全过时的——像iostreams 这样的东西仍然应该通过引用返回。
    • 哇...看来我已经落后于时代了。我只是查找了移动语义。
    • 请再澄清一下。我在 Linux 和 MSVC++ 2010 上使用 gcc-4.1.2;所以 RVO 会为我申请这些编译器?
    猜你喜欢
    • 2016-12-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-15
    • 1970-01-01
    • 1970-01-01
    • 2017-09-06
    • 2015-05-31
    相关资源
    最近更新 更多