代码之家  ›  专栏  ›  技术社区  ›  Rob

是否值得在析构函数中将指针设置为NULL?

c++
  •  16
  • Rob  · 技术社区  · 14 年前

    class Foo
    {
    public:
      Foo() : bar(new Bar)
      {
      }
    
      ~Foo()
      {
        delete bar;
      }
    
      void doSomething()
      {
        bar->doSomething();
      }
    
    private:
      Bar* bar;
    };
    

    除了删除析构函数中的对象之外,还值得将它们设置为NULL吗?

    我假设在上述示例的析构函数中将指针设置为NULL是浪费时间。

    12 回复  |  直到 14 年前
        1
  •  28
  •   In silico    14 年前

    NULL 之后。

    在任何其他情况下,我总是设置一个指向 打电话后 delete 在上面。

        2
  •  38
  •   Michael Burr    14 年前

    有几个答案提到,在调试构建中这样做可能有助于调试。

    不要这样做 .

    如果要“清除”dtor中的指针,最好使用另一种习惯用法—将指针设置为 已知错误指针值

    说吧 doSomething() 看起来像:

    void doSomething()
    {
        if (bar) bar->doSomething();
    }
    

    bar 如果存在对已删除文件的悬空引用,则设置为NULL有助于隐藏错误 Foo 调用的对象 Foo::doSomething() .

    ~Foo()
    {
        delete bar;
        if (DEBUG) bar = (bar_type*)(long_ptr)(0xDEADBEEF);
    }
    

    你可能会有更好的机会抓住这个错误(虽然只是离开)

    现在如果有什么东西提到 已删除的对象,是否使用 不会因为空检查而避免引用它-它会很高兴地尝试使用指针,您会得到一个崩溃,您可以修复它,而不是在调试版本中没有任何不好的事情发生,但是悬挂引用仍然在您客户的发布版本中使用(产生不良影响)。

    当您在调试模式下编译时,调试堆管理器很可能已经为您完成了这项工作(至少MSVC的调试运行时堆管理器将用0xDD覆盖释放的内存,以指示内存已死/已释放)。

    这个规则可能也适用于其他原始指针,但这完全取决于指针的使用方式。

        3
  •  7
  •   Artelius    14 年前

        4
  •  5
  •   peterchen    14 年前

    你不应该,因为两个原因:

    • 在大型应用中,它可能会显著降低关机性能。 在最坏的情况下,关闭应用程序意味着调用几十个不同的析构函数,并写入当前交换到磁盘的数百个堆页。

        5
  •  4
  •   Matthieu M.    14 年前

    首先,这是一个C实践,也是一个有争议的实践。一些人认为(对于C)它隐藏了如果不使用它会更快出现的错误,并且不可能区分使用释放的内存部分和使用从未分配的内存部分。。。

    现在C++中?它是没用的,但原因和C不一样。

    在C++中使用的是一个bug delete . 如果您使用智能指针,您就不会担心这个问题,也不会有泄漏的风险(即:您确定您的复制构造函数和赋值运算符是异常安全的吗?线程安全?)

    最后,在析构函数中它真的是完全无用的。。。一旦析构函数运行,访问对象的任何字段都是未定义的行为。你已经释放了内存,所以很可能有其他东西写在那里,覆盖你小心放置的空值。事实上,内存分配器的工作方式通常是首先重新分配新释放的区域:这将提高缓存性能。。。更是如此(哈哈!)当然,如果我们说的是堆叠的话。

    SAFE_DELETE

        6
  •  2
  •   Viktor Sehr    14 年前

        7
  •  1
  •   Blair Holloway    14 年前

    一般来说,不需要设置指向 NULL

    一个常见的习语是声明 SAFE_DELETE 宏,删除并设置指针 为你:

    #define SAFE_DELETE(x) delete (x); x = NULL
    
    SAFE_DELETE(bar)
    

        8
  •  1
  •   Aoi Karasu    14 年前

    在调试模式下,这是值得的。我确实觉得它经常有用。在发布模式下,由于代码优化,编译器通常会跳过它,因此您不应该在生产代码中真正依赖它。

        9
  •  1
  •   paulm    11 年前

    我会不惜一切代价避免使用原始指针,例如在有或没有smart point的情况下,显式地将其设置为NULL非常有用:

     class foo
     {
       public:
          foo() : m_something( new int ) { }
    
          void doStuff()
          {
              // delete + new again - for whatever reason this might need doing
              m_something.reset( new int );
          }
    
       private:
          std::unique_ptr<int> m_something; // int as an example, no need for it to be on the heap in "real" code
     }
    

    没有:

    class foo
    {
       public:
         foo() : m_something( new int ) { }
         ~foo()
         {
            delete m_something;
         }
    
         void doStuff()
         {
            delete m_something;
    
           // Without this, if the next line throws then the dtor will do a double delete
           m_something = nullptr;
    
           m_something = new int;
         }
    
      private:
         int* m_something
     }
    
        10
  •  0
  •   INS    14 年前

    不,这不值得。

    但如果你想保持一致,那么你可能应该这样做。

    我通常会做的是创建一个 免费的 每次需要释放分配的数据时可以使用的函数(如果需要,可以使用更多的事件自由函数)。在这些函数中,建议您将指针设置为NULL(或标识符设置为无效值)。

        11
  •  0
  •   TortoiseTNT    14 年前

    我认为这样做总是值得的(即使从技术上来说你不需要这么做)。我将指针设置为NULL,以指示它指向的内存不需要释放。

    如果在使用它之前检查指针是否有效也很有用。

    if (NULL == pSomething)
    {
      // Safe to operate on pSomething
    }
    else
    {
      // Not safe to operate on pSomething
    }
    

    在if条件中,将NULL放在第一位可以防止在滑出第二个“=”时将pSomething设置为NULL。你得到的是一个编译错误,而不是一个需要时间来追踪的bug。

        12
  •  -1
  •   humbagumba    14 年前

    最好的做法是在删除指针后将其设置为NULL。一些代码检查工具也强制执行这一点。