假设我在 C++ 中有一个函数 A(),它调用另一个函数 B()。 B() 打开一个文件并读取一个较长的字符串,然后将该字符串返回给 A()。然后 A() 使用此字符串作为 C() 的输入参数。 C() 需要对字符串的引用。
为此,我看到了两个解决方案:
B() 获取对字符串的引用作为输入参数(称之为 str)。 B() 编辑 str,因此 A() 获取字符串。释放分配的内存是 A() 的责任。当 A() 使用字符串完成时,它会将内存返回给系统。我很清楚这一点。
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/