在我的 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
)。但是,我担心的是,我尝试过的任何工具都没有捕获此错误:
-O0 -fsanitize=address -fno-omit-frame-pointer
进行编译时,程序在运行时不会显示任何错误。MALLOC_CONF=junk:true LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libjemalloc.so.2
)运行程序不会在运行时导致任何错误行为。我预计 junk:true
选项会导致释放的内存充满垃圾数据,从而使错误很容易被发现。更奇怪的是:如果我从代码中删除
std::weak_ptr
分配(即禁用标有 (1)
的行),所有三个工具都会正确检测到该错误!
问题:我该怎么做才能让运行时分析工具检测到这个错误?而且,出于好奇:为什么
std::weak_ptr
能够很好地防止检测到错误?
首先您必须了解如何
std::make_shared
。
当您使用 is 时,它会分配比您的对象更大的内存,这个额外的大小包含处理引用计数和销毁过程所需的信息。
现在
shared_ptr
和 weak_weak
指针都使整个内存块保持活动状态。当所有 shared_ptr
结束它们的生命周期时,所做的只是调用指针对象的析构函数。内存保持不变,仍然需要正弦引用计数来处理弱指针。
所以在这种情况下对象被销毁了,但内存仍然存在。 int
是特殊情况,因为它有简单的析构函数,因此您可以在不调用 UB 的情况下访问此内存。如果指向类型没有简单的析构函数,那么访问此内存将调用未定义的行为。