如果从理论上讲,设想一种偶然或错误的情况(无论是由于粗心大意还是由于缺乏经验等),都使用delete-exprssion两次释放了内存(是的,好的开发人员不允许在设计良好的体系结构解决方案中,但是如何)如何(我们,或者有什么方法)可以安全地处理这种情况,以使应用程序不会崩溃?
答案 0 :(得分:8)
发明了智能指针来解决此问题。
如果对象X
由对象Y
拥有,则Y
应该具有std::unique_ptr<X>
而不是原始指针。无需删除,X
被销毁(或决定释放指针)时,Y
将被清除。
如果在某些对象之间共享对象X
,则每个对象都应具有std::shred_ptr<X>
的副本。再次不需要删除。
您唯一需要考虑的是:谁拥有什么以及何时拥有?
编辑:(以满足字面问题)不,您对此无能为力。双重删除是UB。地狱的第六圈。尽管您可以尝试捕获段错误(通常是由两次删除引起的),但这只会增加您的痛苦,因为无论如何您都处于不确定的空间中。安全地处理它的唯一方法是摆脱它。
答案 1 :(得分:6)
使应用程序不会崩溃
这只是一个副作用,是实际问题的实现细节。根据标准,重复删除会导致未定义的行为。
无法将表现出未定义行为的程序变回已定义状态,所以不能安全处理它。
理想情况下,您根本不应该使用new
或delete
。
答案 2 :(得分:6)
除非delete p; delete p;
是p
,否则nullptr
的行为是未定义。
在nullptr
之后将已删除的指针设置为delete
是一种方法:如果{{1},则C ++标准要求delete p;
和delete[] p;
为空操作}是p
。换句话说nullptr
是定义明确的。
但这会导致代码混乱。
除了要多加注意的多方面建议外,使用delete p; p = nullptr; delete p; p = nullptr;
和std::unique_ptr
之类的智能指针类可以消除对显式std::shared_ptr
的需求。
答案 3 :(得分:2)
在某些情况下,您可能需要new
和delete
。在实践中,“仅使用智能指针”就可以了。特别是对于旧版软件。如果必须delete
一个对象,则最好事后将其设置为null(并在删除之前进行null检查)。
someObject *myptr = new someObject;
..... elsewhere in code to free .....
if (myptr) {
delete myptr;
myptr = nullptr;
}
这确保指针下的对象仅被删除一次。
答案 4 :(得分:1)
方法是提供自己的operator new
和operator delete
版本。 operator new
通过调用malloc()
获取内存,而operator delete()
通过调用free()
释放内存。这几乎就是标准库所做的。但是您可以做更多。 operator new()
获取内存,然后将其获取的地址添加到分配的地址列表中。 operator delete()
首先检查传递给它的指针是否在分配的地址列表中。如果是这样,它将从列表中删除它并释放内存。如果没有,它将引发异常。
您不喜欢表演。
答案 5 :(得分:0)
不。你不能那样做。您可以做什么(作为一种好的做法):
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
}
答案 6 :(得分:0)
您可以通过重写new
和delete
运算符来获取给定类型的内存管理的所有权。这并不简单(例如,重写数组分配),Scott Meyers的有效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;
}
使用c++ Apple LLVM version 10.0.0 (clang-1000.11.45.5)
在OSX上运行将返回:
Newing: 0x7fc384c02ab0
Deleting: 0x7fc384c02ab0
Not deleting: 0x7fc384c02ab0
答案 7 :(得分:-1)
我寻求答案! 对于这个问题,唯一的答案是:不。这是未定义的行为。