有什么方法可以通过删除表达式安全地处理两次释放内存?

Is there any method to safe handle twice freeing memory by delete-expression?

本文关键字:两次 释放 内存 处理 可以通过 方法 什么 删除 表达式 安全      更新时间:2023-10-16

如果理论上想象这样一种情况,即偶然或错误(由于粗心大意,或由于缺乏经验等),使用删除-exprssion释放内存两次(是的,在精心设计的架构解决方案中,一个好的开发人员不会允许这样做,但是howewer)如何(我们可以,或者有任何方法可以)安全地处理这种情况,以便应用程序不会崩溃?

智能指针就是为了解决这个问题而发明的。

如果对象X归对象Y所有,则Y应具有std::unique_ptr<X>而不是原始指针。无需删除,X将在销毁Y(或决定释放指针)时进行清理。

如果对象X在某些对象之间共享,则每个对象都应具有std::shred_ptr<X>的副本。同样不需要删除。

您唯一应该考虑的是:谁拥有什么以及何时拥有?

编辑:(满足字面问题)不,你对此无能为力。双重删除是 UB。地狱的第六圈。虽然你可以尝试捕捉段错误(这通常是由双重删除引起的),但这只会增加你的折磨,因为你无论如何都在一个未定义的空间。安全处理它的唯一方法是摆脱它。

以便应用程序不会崩溃

这只是一个副作用,是真正问题的实现细节。根据标准,双重删除会导致未定义的行为。

没有办法将表现出未定义行为的程序变回已定义的程序,所以不,没有办法安全地处理它。


理想情况下,您根本不应该使用newdelete

除非pnullptr,否则delete p; delete p;的行为是未定义的

将已删除的指针设置为nullptr遵循delete是一种方式:C++标准要求delete p;,如果pnullptr,则delete[] p;为无操作。换句话说,delete p; p = nullptr; delete p; p = nullptr;是明确定义的。

但这可能会导致代码凌乱。

除了要多加小心的有趣建议之外,使用std::unique_ptrstd::shared_ptr等智能指针类消除了对显式delete的需要。

在某些情况下,您可能需要newdelete。"只使用智能指针"在实践中可以是一个警察。尤其是对于传统软件。如果必须delete对象,最好在之后将其设置为 null(并在删除之前进行 null 检查。

someObject *myptr = new someObject;
..... elsewhere in code to free .....
if (myptr) {
delete myptr;
myptr = nullptr;
}

这可确保指针下的对象仅删除一次。

这样做的方法是提供您自己的operator newoperator delete版本。operator new通过调用malloc()来获取内存,operator delete()通过调用free()来释放内存。这几乎就是标准库所做的。但你可以做得更多。operator new()获取内存,然后将它获得的地址添加到分配的地址列表中。operator delete()首先检查传递给它的指针是否在分配的地址列表中。如果是这样,它会将其从列表中删除并释放内存。如果不是,则会引发异常。

你不会喜欢这种表演。

No.你不能这么做。你可以做什么(作为一个好的做法是):

char *some_pointer = new char[100];
// use the pointer
strcpy(some_pointer, "this is it");
printf("%s", some_pointer);
// ALWAYS NULL the POINTER after DELETE
delete(some_pointer);
some_pointer = NULL;

因此,每当要为指针分配内存时,都可以仔细检查:

if (some_pointer != NULL) {
// allocate
} else {
// THIS IS SOME KIND OF BUG, can't continue
}

您可以通过重写newdelete运算符来获得给定类型的内存管理的所有权。这并不简单(例如覆盖数组分配),Scott Meyers Effective C++ 书籍对此有一些很好的材料(例如第 16 项),但我相信理论上它会让你能够删除指针两次。

我不确定标准对此的看法是什么,所以我怀疑正如许多人已经说过的语言领域一样,两次删除相同的对象仍然是未定义的行为。

不过,为了讨论...

#include <iostream>
#include <unordered_set>
namespace {
class DeleteMeTwice
{
static std::unordered_set<void *> deleted;
public:
void * operator new (std::size_t count)
{
void * const result = ::operator new(count);
std::cout << "Newing: " << result << "n";
return result;
}
void operator delete(void *ptr)
{
if (is_deleted(ptr))
{
std::cout << "Not deleting: " << ptr << "n";
}
else
{
std::cout << "Deleting: " << ptr << "n";
::operator delete(ptr);
deleted.insert(ptr);
}
}
static bool is_deleted(void *ptr)
{
return deleted.find(ptr) != deleted.end();
}
};
std::unordered_set<void *> DeleteMeTwice::deleted;
}
int main(int argc, char **argv)
{
DeleteMeTwice * const ptwice = new DeleteMeTwice;
delete ptwice;
delete ptwice;
return 0;
}

在 OSX 上运行,返回c++ Apple LLVM version 10.0.0 (clang-1000.11.45.5) Newing: 0x7fc384c02ab0 Deleting: 0x7fc384c02ab0 Not deleting: 0x7fc384c02ab0

我想知道答案! 对于这个问题,唯一的答案是:不。这是未定义的行为。