【问题标题】:double free or corruption while deleting an object删除对象时双重释放或损坏
【发布时间】:2012-01-01 13:05:20
【问题描述】:

我有一个名为“Packet”的类,带有这个破坏:

class Packet
{
   ...
   RequestPtr req;
   ~Packet()
   {
     if (req && isRequest() && !needsResponse())
        delete req;      
     deleteData();
   }
};

RequestPtr 看起来像:

typedef Request* RequestPtr;
class Request
{
   ...
   ~Request() {} 
}

问题是当delete req;~Request() {} 被执行时,我得到这个错误:

*** glibc detected *** double free or corruption (fasttop): 0x0000000002a8a640 ***

一开始我以为req可能在别的地方被删除了,当它要执行delete req;时,显然没有req。但是正如您所见,有一个if 语句检查req 是否已定义。所以肯定当它想删除req时,对象就被定义了。

这个错误的真正含义是什么?

【问题讨论】:

  • 所有if (req) 测试是req 是否为空。如果它已经被释放,或者指向一个随机的(例如未初始化的)内存位置,则该测试将通过。
  • 如果req 被释放,那么我认为它是空的,所以if (req) 应该会失败。不是吗?删除对象和null有什么区别?
  • 不,释放 req 不会使其变为空。如果你想要那种行为,你必须自己做。 free reqreq 指针本身没有影响,只会影响它指向的内容(如果它指向正确类型的有效、活动对象)。
  • 不,您必须在delete 之后将req 设置为NULL。 delete 会为你做这件事。

标签: c++ destructor glibc


【解决方案1】:

Packet 类中使用了默认的复制构造函数和赋值运算符,并且您有一个指向动态分配内存的指针。

如果创建了Packet 的副本并且原始对象被销毁,则在第二个对象被销毁时将发生双重释放。要么实现复制构造函数和赋值运算符,要么通过声明 private 来防止复制 Packet

如果 req 不为 NULL,则检查 if (req) 将为真,如果它已被释放(如 Mat 在问题评论中所述),则不会。

如果你 delete req 在类的其他方法中,你必须将 req 设置为 NULL:

delete req;
req = 0;

否则会发生双重释放。

请注意,在 NULL 指针上调用 delete 无效,因此以下是安全的:

delete req;
req = 0;
delete req; // No need to check 'if (req)'

【讨论】:

  • 或使用 shared_ptr
【解决方案2】:

正确的删除方式是

delete req;
req = 0;

否则req会在删除后成为悬空指针。

【讨论】:

  • 这真是个糟糕的建议。除其他问题外,如果两个指针指向同一个对象,它会产生一种可怕的思维方式。
  • @DavidSchwartz 为什么这是个坏建议?这篇文章是被编辑过还是我遗漏了什么?
  • 这是个糟糕的建议,因为它会产生一种心态,即如果指针的值不为 NULL,则它指向的东西是有效的。如果你想在 C++ 中实现这种行为,你应该创建一个实现它的类,而不是尝试将delete 操作与NULLs 配对。这只是一种残暴的风格,并导致更多关于指针如何工作的混乱。 (一旦两个指针指向同一个对象,它就会严重中断。)
  • @DavidSchwartz 好的,我明白你的意思。你的第一条评论不是很清楚。干杯。
  • 它也没有解决这个确切的问题,这是由 OP 有两个指向同一个对象的指针然后在它们两个上调用 delete 引起的。 (事实上​​,它助长了这个问题。)
【解决方案3】:

我在负责清理其他对象的类中使用这个宏:

// place in a commonly included .h file of yours

#define SAFE_DELETE(p) \
 {if (p != NULL) \
     delete p;\
 p = NULL;}

在cpp文件中的用法:

~Packet() {
    SAFE_DELETE(req)
}

它检查指针是否为非NULL,如果是则删除指针,并将其设置为NULL,这样同样的事情就不会再发生了。

【讨论】:

  • if (p != NULL) 没有用,因为 delete-ing NULL 指针是有效的。
  • @Cicada 你今天在我的 Windows 系统上是对的。也许当我第一次开始使用这个宏时,它是必要的。堆管理器在过去是否因“删除 NULL”而崩溃?我仍然会使用没有 if 的宏,以便将指针设置为 NULL。
  • 据我所知,delete-ing NULL 一直是合法的,甚至回到 C 中,free(NULL) 的作用就像一个魅力。
  • 等一下。检查 NULL 是否比调用 delete 更便宜?
  • 这是一个糟糕的建议,原因有很多。除其他外,它在示例用法中完全没有完成任何事情——变量在它不复存在之前被设置为 NULL。 (这也是世界上最不安全的事情。想想SAFE_DELETE(list->getHeadItem());。很酷,我们删除了头项,然后将 new 头项设置为 NULL?!)
猜你喜欢
  • 2012-02-08
  • 2023-03-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多