有什么方法可以安全地处理两次通过delete-expression释放内存的情况吗?

时间:2019-02-27 12:43:14

标签: c++

如果从理论上讲,设想一种偶然或错误的情况(无论是由于粗心大意还是由于缺乏经验等),都使用delete-exprssion两次释放了内存(是的,好的开发人员不允许在设计良好的体系结构解决方案中,但是如何)如何(我们,或者有什么方法)可以安全地处理这种情况,以使应用程序不会崩溃?

8 个答案:

答案 0 :(得分:8)

发明了智能指针来解决此问题。

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

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

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

编辑:(以满足字面问题)不,您对此无能为力。双重删除是UB。地狱的第六圈。尽管您可以尝试捕获段错误(通常是由两次删除引起的),但这只会增加您的痛苦,因为无论如何您都处于不确定的空间中。安全地处理它的唯一方法是摆脱它。

答案 1 :(得分:6)

  

使应用程序不会崩溃

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

无法将表现出未定义行为的程序变回已定义状态,所以不能安全处理它。


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

答案 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)

在某些情况下,您可能需要newdelete。在实践中,“仅使用智能指针”就可以了。特别是对于旧版软件。如果必须delete一个对象,则最好事后将其设置为null(并在删除之前进行null检查)。

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

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

答案 4 :(得分:1)

方法是提供自己的operator newoperator 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)

您可以通过重写newdelete运算符来获取给定类型的内存管理的所有权。这并不简单(例如,重写数组分配),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)

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