有什么方法可以通过删除表达式安全地处理两次释放内存?
Is there any method to safe handle twice freeing memory by delete-expression?
如果理论上想象这样一种情况,即偶然或错误(由于粗心大意,或由于缺乏经验等),使用删除-exprssion释放内存两次(是的,在精心设计的架构解决方案中,一个好的开发人员不会允许这样做,但是howewer)如何(我们可以,或者有任何方法可以)安全地处理这种情况,以便应用程序不会崩溃?
智能指针就是为了解决这个问题而发明的。
如果对象X
归对象Y
所有,则Y
应具有std::unique_ptr<X>
而不是原始指针。无需删除,X
将在销毁Y
(或决定释放指针)时进行清理。
如果对象X
在某些对象之间共享,则每个对象都应具有std::shred_ptr<X>
的副本。同样不需要删除。
您唯一应该考虑的是:谁拥有什么以及何时拥有?
编辑:(满足字面问题)不,你对此无能为力。双重删除是 UB。地狱的第六圈。虽然你可以尝试捕捉段错误(这通常是由双重删除引起的),但这只会增加你的折磨,因为你无论如何都在一个未定义的空间。安全处理它的唯一方法是摆脱它。
以便应用程序不会崩溃
这只是一个副作用,是真正问题的实现细节。根据标准,双重删除会导致未定义的行为。
没有办法将表现出未定义行为的程序变回已定义的程序,所以不,没有办法安全地处理它。
理想情况下,您根本不应该使用new
或delete
。
除非p
nullptr
,否则delete p; delete p;
的行为是未定义的。
将已删除的指针设置为nullptr
遵循delete
是一种方式:C++标准要求delete p;
,如果p
nullptr
,则delete[] p;
为无操作。换句话说,delete p; p = nullptr; delete p; p = nullptr;
是明确定义的。
但这可能会导致代码凌乱。
除了要多加小心的有趣建议之外,使用std::unique_ptr
和std::shared_ptr
等智能指针类消除了对显式delete
的需要。
在某些情况下,您可能需要new
和delete
。"只使用智能指针"在实践中可以是一个警察。尤其是对于传统软件。如果必须delete
对象,最好在之后将其设置为 null(并在删除之前进行 null 检查。
someObject *myptr = new someObject;
..... elsewhere in code to free .....
if (myptr) {
delete myptr;
myptr = nullptr;
}
这可确保指针下的对象仅删除一次。
这样做的方法是提供您自己的operator new
和operator 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
}
您可以通过重写new
和delete
运算符来获得给定类型的内存管理的所有权。这并不简单(例如覆盖数组分配),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
我想知道答案! 对于这个问题,唯一的答案是:不。这是未定义的行为。
- g++的分段错误(在NaN上使用to_string两次时)
- 蛇在C++不会连续转两次
- 检查一个数组是否包含在另一个数组中,以相反的顺序,至少两次
- 从具有按值捕获的 lambda 移动构造 std::函数时,移动构造函数调用两次
- 我应该如何去缓解两次出现的cin?
- Realloc 两次无法在 Visual Studio 上运行
- 使用 getline(cin, var) 两次在进行字符串比较时会产生错误 (==)
- 为什么映射插入和 map.find() 的单次迭代比插入和 map.find() 的两次单独迭代慢得多
- 如果包含映射的静态库与可执行文件和动态库链接,静态映射(变量)是否会被多次释放?
- C++析构函数调用两次,堆栈分配的复合对象
- 为什么参数在构造 std::thread 时移动两次
- Qt插槽调用了两次
- 做 std::用相同的unique_ptr移动两次
- C++两次定义相同的函数会导致错误
- 为什么具有静态存储持续时间的同一内联变量在包含在 VS2017 编译的两个翻译单元中时会构造和销毁两次
- 对于优化级别为 0 的 std::vector,析构函数被调用两次
- 使用柯南打包时如何避免列出两次依赖?
- 为什么要执行两次位移((x >> 4)<< 4)?
- 有什么方法可以通过删除表达式安全地处理两次释放内存?
- Java - Can调用系统.Gc两次,释放底层对象两次