c++ - 在 C++ 中,大返回值(例如字符串)的内存释放是如何发生的?

标签 c++ string memory-management return-value return-value-optimization

假设我在 C++ 中有一个函数 A(),它调用另一个函数 B()。 B() 打开一个文件并读取一个较长的字符串,然后将该字符串返回给 A()。然后 A() 使用此字符串作为 C() 的输入参数。 C() 需要对字符串的引用。

为此,我看到了两个解决方案:

  1. B() 获取对字符串的引用作为输入参数(称之为 str)。 B() 编辑 str,因此 A() 获取字符串。释放分配的内存是 A() 的责任。当 A() 使用字符串完成时,它会将内存返回给系统。我很清楚这一点。

  2. B() 返回一个字符串。我对这个选项感到困惑。那么谁释放分配的内存呢? B() 创建一个本地字符串,因此不能返回它的地址。 B() 必须按值返回参数。据我所知,由于返回值优化,不会创建字符串的实际拷贝。在后台,参数仍然通过引用传递。当我写这样的伪代码时:

A() {
  C(B(filename))
}

分配的内存会发生什么? B() 获取文件名,打开文件,为它从文件中读取的字符串分配一个内存块,然后返回这个字符串。 B()的生命范围到此结束。 A() 没有定义用于寻址此字符串的变量。谁还给这个内存块? 我可以依赖 RVO 吗?业绩按值(value)返回比可以吗?每个编译器都有吗?

最佳答案

是否使用 RVO 是一种优化,而且是一种透明的优化。我将在最后介绍它,因为它对按值返回对象的机制没有影响。

现在当你按值返回一个对象时,编译器实际上做的是添加一个不可见的参数作为指针。该内存分配在调用者的堆栈上。您的函数使用编写的代码执行操作(记住,没有 RVO),然后返回使用本地对象调用调用者对象上的复制构造函数的对象。然后本地的被销毁。

谁清除调用者对象?与所有其他对象一样,它遵循 RAII 规则:它的析构函数在作用域结束时被调用,堆栈指针在函数结束时倒回过去。

现在谈谈 RVO。 RVO 所做的只是避免了函数中的内部创建+复制+析构函数调用——它直接与函数外部的对象一起工作。它将在其上调用适当的构造函数(考虑放置 new),然后它将使用其字段和函数来完成工作,最后没有什么可做的。它不会被销毁或释放,因为这就是结果——而且它已经在调用者手中。

编辑:至于 RVO 有多常见,任何理智的 PC 编译器都支持它。它与 #pragma once 一样受到支持,尽管古怪的纯粹主义者总是说它不是“标准”。

关于c++ - 在 C++ 中,大返回值(例如字符串)的内存释放是如何发生的?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28222461/

相关文章:

c++ - 在 C++ 中实现工厂方法的首选方法是什么?

java - 构建 .dll 时找不到 GCC 选项

java - 我无法在 String.format 上使用变量

c++ - 表达式必须是可修改的左值(字符数组)

java - 在 Java 中使用 return string.format(...) 格式化字符串

regex - 如何使用正则表达式匹配亚洲字符?

memory-management - 如何在现有的 Haskell 代码中从 String 转到 Data.ByteString.Lazy?

ruby - 无法在 Ruby 中分配内存(无 MemoryError)?

c++ - 如何创建一个QJsonDocument?

c++ - 数据结构是否适合放置 shared_ptr?