【问题标题】:How to safely delete multiple pointers如何安全地删除多个指针
【发布时间】:2010-10-14 10:53:52
【问题描述】:

我有一些代码使用大量指向同一地址的指针。 给出一个等效的简单示例:

int *p =  new int(1);
int *q = p;
int *r = q;

delete r; r = NULL; // ok
// delete q; q = NULL; // NOT ok
// delete p; p = NULL; // NOT ok

如何在不多次删除的情况下安全删除? 如果我有很多指针都指向同一个地址的对象,这尤其困难。

【问题讨论】:

  • 不应该这样工作吗?标准中指定了 delete null ,因此它允许并且应该工作。好吧,这不是最好的编码风格......
  • @Mario:删除 NULL 被指定为 NO-OP,但调用它确实会产生一些开销。
  • 问题是q,p不会为NULL,所以会有双重删除。
  • @Mario 当您删除 qp 时,您没有在 NULL 上调用 delete;您正在对该整数的旧地址调用 delete,qp 仍在保留。
  • 哎呀,不应该在午餐后不久发布...谢谢纠正我!

标签: c++ pointers memory delete-operator


【解决方案1】:

您的工具是boost 库的shared_ptr。看看文档:http://www.boost.org/doc/libs/1_44_0/libs/smart_ptr/shared_ptr.htm

例子:

void func() {
  boost::shared_ptr<int> p(new int(10));
  boost::shared_ptr<int> q(p);
  boost::shared_ptr<int> r(q);

  // will be destructed correctly when they go out of scope.
}

【讨论】:

  • +1 - OP 的“大量指针指向同一个地址”的场景非常适合 shared_ptr
  • 最近发布的编译器提供 shared_ptr 作为 tr1 命名空间的成员。所以 boost 不是必需的——你可以使用 tr1::shared_ptr 来代替。
  • C++ 中的“原始”指针继承自 C,但问题是它们根本不表示所有权(这是不能经常被提及,恕我直言)。关于代码最重要的事情之一是它必须表达作者的意图(包括所有权),因此使用 boost 智能指针总是更好,或者至少是 std::auto_ptr。 (但老派指针适用于简单的情况,例如“平凡”对象的内部结构)。
  • @riviera:+1,在大多数指针讨论中似乎忘记的最重要的词是所有权
  • @kotlinski:虽然我也使用 cmets,但我更喜欢代码来表达其意图。一个明显的优势是,一旦编译器“知道”您想要做什么,它就可以应用其类型安全检查等(更不用说“您不是为计算机编写代码,而是为计算机编写代码)其他人”)。但我不想太学术,我已经在我之前的评论中说过最重要的东西:)
【解决方案2】:

答案是,在不使用托管指针的情况下,您应该知道是否根据指针的分配位置来删除它。

您的示例有点做作,但在现实世界的应用程序中,负责分配内存的对象将负责销毁它。接收已经初始化的指针并将它们存储一段时间的方法和函数不会删除这些指针;该责任在于最初分配内存的任何对象。

请记住,您对new 的调用应该与您对delete 的调用相平衡。每次分配内存时,您知道您必须编写平衡代码(通常是析构函数)来释放该内存。

【讨论】:

  • +1:我的观点是 shared_ptr 应该只用在没有明确的内存所有者并且在大多数情况下你不需要它的极端情况下。
  • 请阅读:crazyeddiecpp.blogspot.com/2010/12/pet-peeve.html - 你不要删除指针!
  • @Noah 谢谢,但不,谢谢。我会继续说“删除指针”而不是“释放指针指向的内存”。
【解决方案3】:

“现代”答案是使用智能指针并且不进行任何手动删除。

boost::shared_ptr<int> p(new int(1));
boost::shared_ptr<int> q = p;
boost::shared_ptr<int> r = q;

故事结束!

【讨论】:

  • -1 用于发布未经测试的代码。一个好的智能指针 API(例如 std::auto_ptr 的 boost::shared_ptr )不允许将指针分配给智能指针或使用复制构造函数从指针隐式创建智能指针。这样所有权就不明显了。将第一行更改为 boost::shared_ptr p = boost::shared_ptr(new int(1));
【解决方案4】:

您面临的问题是程序中的所有权语义不清楚。从设计的角度来看,尝试在每一步确定谁是对象的所有者。在许多情况下,这意味着创建对象的人稍后必须将其删除,但在其他情况下,所有权可以转移甚至共享。

一旦您知道谁拥有内存,然后返回代码并实现它。如果一个对象是另一个对象的唯一负责人,它应该通过单一所有权智能指针 (std::auto_ptr/unique_ptr) 甚至是原始指针(尽量避免这种情况,因为它是常见的错误)并手动管理内存。然后将引用或指针传递给其他对象。当所有权转移时,使用智能指针工具将对象让给新所有者。如果所有权是真正共享的(分配对象没有明确的所有者),那么您可以使用shared_ptr 并让智能指针处理内存管理)。

【讨论】:

    【解决方案5】:

    你为什么要随意删除指针?每个动态分配的对象都由 one 所有者分配在 one 位置。确保再次删除该对象应该是一个所有者的责任。

    在某些情况下,您可能希望将所有权转移给另一个对象或组件,在这种情况下,删除的责任也会发生变化。

    有时,您只想忘记所有权并使用共享所有权:使用对象的每个人都共享所有权,只要至少存在一个用户,就不应删除该对象。

    然后你使用shared_ptr

    简而言之,使用 RAII。不要尝试手动删除对象。

    【讨论】:

      【解决方案6】:

      在极少数情况下,您可能无法使用智能指针(可能是处理旧代码),但也无法使用简单的“所有权”方案。

      假设您有一个 std::vector&lt;whatever*&gt; 和一些 whatever* 指针指向同一个对象。安全清理涉及确保您不会两次删除相同的内容 - 因此请随时构建std::set&lt;whatever*&gt;,并且只删除尚未在集合中的指针。一旦所有指向的对象都被删除,这两个容器也可以安全地删除。

      insert 的返回值可用于确定插入的项目是否是新的。我还没有测试以下(或使用 std::set 一段时间),但我认为以下是正确的......

      if (myset.insert (pointervalue).second)
      {
        //  Value was successfully inserted as a new item
        delete pointervalue;
      }
      

      当然,你不应该把项目设计成这样是必要的,但如果你无法避免,处理这种情况并不难。

      【讨论】:

        【解决方案7】:

        不可能知道指针引用的内存是否已经被删除,就像你的情况一样。
        如果您不能使用带有智能指针的库来执行引用计数,并且您不能实现自己的引用计数模式(如果确实需要执行您在帖子中描述的操作),请尝试在指针上调用 realloc。
        我在其他帖子中读到,根据实现,对 realloc 的调用可能不会崩溃,但会返回一个空指针。在这种情况下,您知道该指针引用的内存已被释放。
        如果这是一个肮脏的解决方案,它将无法移植,但如果您别无选择,请尝试一下。更糟糕的当然是让你的应用程序崩溃:)

        【讨论】:

          【解决方案8】:

          我会说,有时,智能指针实际上会减慢您的应用程序的速度,但不会减慢很多。我要做的是创建一个方法,如下所示:

          void safeDelete(void **ptr)
          {
            if(*ptr != NULL)
            {
               delete ptr;
               *ptr = NULL;
            }
          }
          

          我不确定我是否 100% 正确执行了此操作,但您所做的是将指针传递给此方法,该方法采用指针的指针,检查以确保它指向的指针未设置为NULL,然后删除对象,然后将地址设置为 0 或 NULL。如果这不是这样做的好方法,请纠正我,我也是新手,有人告诉我这是一种很好的检查方法,不会变得复杂。

          【讨论】:

          • 这并不能解决问题;对于问题中的示例,safeDelete(&amp;r) 可以工作,但不会修改qp,因此safeDelete(&amp;q) 可能会崩溃。
          猜你喜欢
          • 2013-08-12
          • 1970-01-01
          • 2010-10-30
          • 2014-12-07
          • 2016-07-22
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多