为什么这个 std::weak_ptr 明显阻止释放内存,以及如何检测这个错误?

问题描述 投票:0回答:1

在我的 C++ 代码中,我有一个

std::shared_ptr
超出了范围,这将其使用计数减少到 0,因此指向的对象被销毁。这很好用。但是,如果我有一个
std::weak_ptr
指向该
std::shared_ptr
并保持活动状态,则指向的内存显然实际上并未被释放。

示例:

#include <iostream>
#include <memory>

int main() {
    int* my_raw_ptr = nullptr;
    std::weak_ptr<int> my_wp;
    {
        auto my_sp = std::make_shared<int>(42);
        std::cout << "my_sp value: " << *my_sp << std::endl;
        my_raw_ptr = &(*my_sp);

        my_wp = my_sp;  // (1)
    }

    std::cout << "my_raw_ptr value: " << *my_raw_ptr << std::endl;

    return 0;
}

(关于 Godbolt:https://godbolt.org/z/Kd46fn8zc

现在,我知道这段代码具有未定义的行为(即,在

my_raw_ptr
超出范围后取消引用
my_sp
)。但是,我担心的是,我尝试过的任何工具都没有捕获此错误:

  • 使用 clang 19.1.0 和
    -O0 -fsanitize=address -fno-omit-frame-pointer
    进行编译时,程序在运行时不会显示任何错误。
  • Valgrind 3.22.0 未显示任何运行时错误。
  • 使用 jemalloc 和“内存垃圾”(即使用
    MALLOC_CONF=junk:true LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libjemalloc.so.2
    )运行程序不会在运行时导致任何错误行为。我预计
    junk:true
    选项会导致释放的内存充满垃圾数据,从而使错误很容易被发现。

更奇怪的是:如果我从代码中删除

std::weak_ptr
分配(即禁用标有
(1)
的行),所有三个工具都会正确检测到该错误!

问题:我该怎么做才能让运行时分析工具检测到这个错误?而且,出于好奇:为什么

std::weak_ptr
能够很好地防止检测到错误?

c++ valgrind undefined-behavior address-sanitizer weak-ptr
1个回答
0
投票

首先您必须了解如何

std::make_shared
。 当您使用 is 时,它会分配比您的对象更大的内存,这个额外的大小包含处理引用计数和销毁过程所需的信息。

现在

shared_ptr
weak_weak
指针都使整个内存块保持活动状态。当所有
shared_ptr
结束它们的生命周期时,所做的只是调用指针对象的析构函数。内存保持不变,仍然需要正弦引用计数来处理弱指针。 所以在这种情况下对象被销毁了,但内存仍然存在。
int
是特殊情况,因为它有简单的析构函数,因此您可以在不调用 UB 的情况下访问此内存。如果指向类型没有简单的析构函数,那么访问此内存将调用未定义的行为。

© www.soinside.com 2019 - 2024. All rights reserved.