使用动态内存来避免引用失效

Joh*_*hnZ 1 c++ memory-management reference

#include <iostream>
#include <vector>
#include <cstring>

using namespace std; //please ignore bad practices like this and everything being set to public. Its just for sake of demonstration.

vector<int>& f() {
    vector<int> a = *new vector<int>{15, 15, 16};
    return a;
}

int main () {
    vector<int>& b = f();
    
    for (auto x: b) {
        cout << x << endl;
    }
delete &b;
    
    cout << "finished" << endl;
}
Run Code Online (Sandbox Code Playgroud)

你好:

在上面的代码中,我使用动态分配的内存,以便向量 a 将其内存存储在堆上,这样就可以避免在函数退出后被删除。但由于某种原因,它没有。该代码给了我这个无法理解的错误:

=================================================================
==22==ERROR: AddressSanitizer: heap-use-after-free on address 0x602000000030 at pc 0x000000354a2c bp 0x7ffc1174eb30 sp 0x7ffc1174eb28
READ of size 4 at 0x602000000030 thread T0
    #1 0x7f2c73349082  (/lib/x86_64-linux-gnu/libc.so.6+0x24082)
0x602000000030 is located 0 bytes inside of 12-byte region [0x602000000030,0x60200000003c)
freed by thread T0 here:
    #3 0x7f2c73349082  (/lib/x86_64-linux-gnu/libc.so.6+0x24082)
previously allocated by thread T0 here:
    #5 0x7f2c73349082  (/lib/x86_64-linux-gnu/libc.so.6+0x24082)
Shadow bytes around the buggy address:
  0x0c047fff7fb0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c047fff7fc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c047fff7fd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c047fff7fe0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c047fff7ff0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x0c047fff8000: fa fa 00 04 fa fa[fd]fd fa fa fa fa fa fa fa fa
  0x0c047fff8010: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c047fff8020: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c047fff8030: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c047fff8040: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c047fff8050: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07 
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
  Shadow gap:              cc
==22==ABORTING
Run Code Online (Sandbox Code Playgroud)

我觉得我错过了一些东西——之后我会更恰当地重命名/改写我的问题。

编辑:更正后的代码如下所示

vector<int>& f() {
    vector<int>& a = *new vector<int>{15, 15, 16};
    return a;
}

int main () {
    vector<int>& b = f();
    
    for (auto x: b) {
        cout << x << endl;
    }
delete &b;
    
    cout << "finished" << endl;
}
Run Code Online (Sandbox Code Playgroud)

Ahm*_*AEK 7

vector<int>& f() {
    vector<int> a = *new vector<int>{15, 15, 16};
    return a;
}
Run Code Online (Sandbox Code Playgroud)

这是调用 的复制构造函数a并将堆分配的对象传递给它。a不是在堆上创建的。它在f()退出时被销毁,使返回的引用悬空,并且堆分配的向量在构造函数之后泄漏。

使用引用而不是第二个对象。只需将堆上已创建的向量绑定到返回的引用即可。

vector<int>& f() {
    vector<int>& a = *new vector<int>{15, 15, 16};
    return a;
}
Run Code Online (Sandbox Code Playgroud)

虽然这可能有效,但这是糟糕的设计

引用是非拥有构造,它不管理对象的生命周期,而您的代码期望delete对象的调用者,这是等待发生的内存泄漏。


使此代码正常工作的正确方法是简单地从函数返回向量并让编译器移动它:

vector<int> f() {
    vector<int> a{15, 15, 16};
    return a;
}
...
vector<int> b = f();
Run Code Online (Sandbox Code Playgroud)

  • 为了完整起见,您可能希望澄清 (1) OP 的代码不仅返回悬空引用(由于“a”的破坏)——它还泄漏了“new”表达式创建的向量。(2) 正如你所说,你的第一个代码示例可以工作,但设计很糟糕 - 这是因为它迫使调用者显式释放“new”向量(通过不必要的笨拙语法从引用获取指针,并且“删除它),或者,如果调用者忽略了这一点,结果是另一个成员泄漏。 (2认同)