大返回值(如字符串)的内存释放如何在C++中发生
How does the memory release for big return values (such as string) happen in C++?
假设我在C++中有一个函数A(),它调用另一个函数B()。B() 打开一个文件并读取一个长字符串,然后将此字符串返回到 A()。Than A() 使用此字符串作为 C() 的输入参数。C() 需要对字符串的引用。
为此,我看到了两种解决方案:
-
B() 获取对字符串的引用作为输入参数(称为 str)。B() 编辑 str,因此 A() 获取字符串。释放分配的内存是 A() 的责任。当 A() 使用字符串完成时,它将内存返回给系统。这对我来说很清楚。
-
B() 返回一个字符串。我对这个选项感到困惑。那么谁释放分配的内存呢?B() 创建一个本地字符串,因此无法返回其地址。B() 必须按值返回参数。据我所知,由于返回值优化,没有创建字符串的实际副本。在后台,参数仍然通过引用传递。当我写这样的伪东西时:
A() { C(B(filename)) }
与分配的内存相比会发生什么?B() 获取文件名,打开文件,为它从文件中读取的字符串分配一个内存块,然后返回此字符串。这是B()生命范围的结束。A() 没有定义一个变量来寻址此字符串。谁把这个内存块还给了?我可以依靠RVO吗?性能可以按值返回吗?每个编译器都有吗?
是否使用 RVO 是一种优化,而且是透明的优化。我将在最后介绍这一点,因为它对按值返回对象的机制没有区别。
现在,当您按值返回对象时,编译器实际上所做的是添加一个不可见的参数作为指针。此内存在调用方的堆栈上分配。您的函数使用编写的代码执行其操作(请记住,没有 RVO),然后返回使用本地对象调用调用者对象上的复制构造函数的对象。然后当地的那个被摧毁了。
谁清除调用方对象?与其他所有对象相同,它遵循 RAII 规则:当作用域结束时调用其析构函数,当函数结束时,堆栈指针会倒退到它之外。
现在关于RVO。RVO所做的只是避免函数中的内部创建+复制+desctructor 调用 - 它直接与函数外部的对象一起工作。它将在其上调用正确的构造函数(想想放置new
),然后它将使用它的字段和函数来完成工作,最后没有什么可做的了。它不会被摧毁或释放,因为这就是结果 - 它已经在调用者手中。
编辑:至于RVO的普遍程度,任何理智的PC编译器都支持它。它和#pragma once
一样得到支持,尽管古怪的纯粹主义者总是说它不是"标准"。
假设使用 RVO,对于您的代码:
A() {
C(B(filename))
}
编译器在 A
的上下文中创建类型 std::string
的临时。它基本上在调用该临时时传递一个隐藏的引用 B
. B
写入引用的字符串,然后返回。
然后将该临时对象传递给 C
。因此,最终代码大致等同于此顺序上的内容:
// string B(); is turned into:
void B(string &ret) {
ret = "a really long string";
}
void A() {
{
std::string temporary_object;
B(temporary_object);
C(temporary_object);
}
}
我在表达式的扩展周围添加了一个额外的块,以强调临时对象是在表达式C(B(filename))
开始执行时创建的,并在表达式结束时再次销毁。
我上面展示的内容在某些方面与真实内容不同。例如,如果你有一个不支持默认构造的类,即使涉及 RVO,它仍然可以工作(上面的代码要求类型是默认可构造的,这对于std::string
很好,但对于其他任何东西可能不太有效)。如果你想更准确一点,你可能会传递一个指向原始内存的指针,B
会使用放置 new 来就地创建返回值(但你可以显示的任何内容都不会C++精确地复制编译器的功能)。
- 释放错误后堆使用
- G锁定铸造到基础上会释放模拟行为
- 在将变量声明为引用时,堆在释放后使用
- 在调用FreeLibrary后,释放动态链接到具有相同版本的CRT堆的DLL的内存
- 正在理解智能指针,但出现错误:未分配正在释放的指针
- C++双重释放或损坏(out)
- 如何在c++中释放内存
- 使用全局声明的向量时,C++双重释放错误/损坏
- 为什么这个 std::queue/指向结构的指针列表直到 List.Size() == 0 才释放内存?
- 为什么瓦尔格林德在不释放恶意内存后没有报告任何问题?
- 调用析构函数以释放动态分配的内存
- 在函数范围内在堆栈上分配的数组在离开函数时是否总是被释放?
- COM :是否可以查看是否存在对我的某个 COM 对象的进程外引用?我可以释放它吗?
- 如何在向量中释放指针?
- std::unordered_map析构函数不释放内存?
- 在C++中释放内存期间,迭代器与指针有何不同
- 包含矢量指针的结构的内存释放问题
- C++:在被本地字符串捕获后释放或销毁 malloc'd char *?
- 错误:malloc:对象 0x7f9edf504080 的 *** 错误:未分配正在释放的指针
- 如果分配数组引发异常,是否应该释放该数组