【问题标题】:Which version of safe_delete is better?哪个版本的 safe_delete 更好?
【发布时间】:2009-02-12 11:29:53
【问题描述】:
#define SAFE_DELETE(a) if( (a) != NULL ) delete (a); (a) = NULL;

template<typename T> void safe_delete(T*& a) {
  delete a;
  a = NULL;
}

或任何其他更好的方法

【问题讨论】:

  • 它仍然很傻,但宏安全删除应该是:#define SAFE_DELETE(a) do { delete (a); (a) = 空; } 而 (0)

标签: c++ pointers c-preprocessor


【解决方案1】:

我不会说两者都不会,因为两者都会给你一种虚假的安全感。例如,假设你有一个函数:

void Func( SomePtr * p ) {
  // stuff
  SafeDelete( p );
}

您将 p 设置为 NULL,但函数外的 p 副本不受影响。

但是,如果您必须这样做,请使用模板 - 宏总是有可能超越其他名称。

【讨论】:

  • +1 在删除指针后将其设置为 NULL 几乎没有任何意义。
  • 在删除后将指针(类的成员)设置为 NULL 可能值得花时间将行添加到模板函数或浪费几个 CPU 周期。如果您必须挖掘由设计不佳的其他人编写的代码,这些代码很容易发现(崩溃)隐藏得很好的怪物:使用已删除对象的对象大约十分之一秒。如果您不使用 Z80,它是值得的。它会保存你的头被炸毁。
【解决方案2】:

很明显的功能,原因很简单。宏多次评估其参数。这可能会产生不良的副作用。该功能也可以限定范围。没有比这更好的了:)

【讨论】:

  • 可以对宏进行作用域。这就是为什么你经常看到宏被包裹在do { } while (0)
【解决方案3】:

删除一个;

ISO C++ 规定,对 NULL 指针的删除不做任何事情。

引自 ISO 14882:

5.3.5 删除[expr.delete] 2 [...] 在任一替代方案中,如果 delete 的操作数的值是 空指针操作无效。 [...]

问候,博多

/edit: 我没有注意到 a=NULL;在原来的帖子中,所以新版本:删除一个;一个=空;但是,已经指出了设置 a=NULL 的问题(错误的安全感)。

【讨论】:

  • 关于 NULL 和性能测试:我知道可以使用 NULL 调用 delete,但是 if(p!=NULL) 是一个快速读取操作,可以跳过函数调用和赋值 (写入内存)。去掉 if 并没有错,但是阅读它的初级程序员呢,性能呢?
  • 没错,另一方面:测试 NULL 是一个条件跳转。如果您以通常不释放 NULL 指针的方式编写代码,则只需复制该条件跳转,因为您自己执行此操作,然后无论如何调用一个函数,它会再次执行此操作。因此,在我看来,最好教进行适当的资源跟踪而不是进行 NULL 测试。
【解决方案4】:

一般来说,内联函数优于宏,因为宏不考虑作用域,并且在预处理过程中可能会与某些符号冲突,从而导致非常奇怪的编译错误。

当然,有时模板和函数是不行的,但这里不是这样。

此外,更好的安全删除不是必需的,因为您可以使用智能指针,因此不需要记住在客户端代码中使用此方法,而是封装它。

(edit) 正如其他人指出的那样,安全删除并不安全,因为即使有人没有忘记使用它,它仍然可能没有想要的效果。所以它实际上是完全没有价值的,因为正确使用 safe_delete 需要更多的思考,而不仅仅是自己设置为 0。

【讨论】:

    【解决方案5】:

    您不需要使用delete 测试无效性,它相当于无操作。 (a) = NULL 让我挑眉。第二种选择更好。

    但是,如果您有选择,您应该使用智能指针,例如 std::auto_ptrtr1::shared_ptr,它们已经为您执行此操作。

    【讨论】:

    • auto_ptr 和 shared_ptr 都不会在删除时设置为 NULL。 QPointer 是我知道的唯一一个这样做的类。
    【解决方案6】:

    我觉得

    #define SAFE_DELETE(pPtr) { delete pPtr; pPtr = NULL } 更好

    1. 如果 pPtr 为 NULL,则可以调用 delete。所以 if 检查不是必需的。
    2. 如果调用SAFE_DELETE(ptr+i),会导致编译错误。
    3. 模板定义将为每种数据类型创建函数的多个实例。在我看来,在这种情况下,这些多重定义不会增加任何价值。
    4. 此外,使用模板函数定义,您有函数调用的开销。

    【讨论】:

    • #4 真的不是问题。这么简单的函数肯定会被任何半体面的编译器内联。
    • #3 不是问题,原因相同。事实上,我认为 #2 也是,因为 ptr + 1 不是左值。所以基本上所有相关点都是无效的。
    • 另外,如果想用宏来做这件事,至少应该使用这个:#define save_delete(p) do { delete p; p=NULL; } while(0) 想想看,如果你还没有见过这样的构造;)
    • 而且#2 也不是问题。 (p+1) 是一个右值,模板采用非常量引用。事实上,更好的编译器错误。宏名称不太可能出现在错误消息中。
    【解决方案7】:

    SAFE_DELETE 的使用实际上似乎是 C 程序员在 C++ 中控制内置内存管理的一种方法。我的问题是:C++ 是否允许在已正确封装为 Private 的指针上使用 SAFE_DELETE 的这种方法?这个宏是否只适用于声明为 Public 的指针?糟糕!

    【讨论】:

    • 这正是 C 版本更好的原因,您会遇到无用的封装问题,最好在预处理器时而不是编译时添加这些东西。
    【解决方案8】:

    如上所述,第二个更好,不是具有潜在意外副作用的宏,没有针对 NULL 的不必要检查(尽管我怀疑您这样做是为了进行类型检查),等等。但两者都没有承诺任何安全性。如果您确实使用了 tr1::smart_ptr 之类的东西,请确保您阅读了它们的文档并确保它具有适合您任务的语义。由于同事将 smart_ptrs 放入具有循环链接的数据结构中,我最近不得不寻找并清理巨大的内存泄漏:)(他应该使用weak_ptrs 进行反向引用)

    【讨论】:

      【解决方案9】:

      我更喜欢这个版本:

      ~scoped_ptr() {
          delete this->ptr_; //this-> for emphasis, ptr_ is owned by this
      }
      

      在删除指针后将其设置为 null 毫无意义,因为使用指针的唯一原因是允许同时在多个位置引用一个对象。即使程序的某一部分的指针为 0,也可能有其他部分未设置为 0。

      此外,safe_delete 宏/函数模板很难正确使用,因为如果在给定指针的 new 和 delete 之间存在可能抛出的代码,它只有两个地方可以使用。

      1) 在重新抛出异常的 catch (...) 块内,并在不抛出异常的路径的 catch (...) 块旁边复制。 (在每个可能允许指针超出范围的中断、返回、继续等旁边也重复)

      2) 在拥有指针的对象的析构函数中(除非在 new 和 delete 之间没有可以抛出的代码)。

      即使在您编写代码时没有可能抛出的代码,这也可能在未来发生变化(只需有人出现并在第一个代码之后添加另一个新代码)。最好以即使在遇到异常时也能保持正确的方式编写代码。

      选项 1 会产生大量代码重复并且很容易出错,以至于我什至怀疑是否将其称为选项。

      选项 2 使 safe_delete 变得多余,因为您设置为 0 的 ptr_ 将超出下一行的范围。

      总而言之——不要使用safe_delete,因为它根本不安全(它很难正确使用,并且即使使用正确也会导致冗余代码)。使用 SBRM 和智能指针。

      【讨论】:

      • 如果你使用 safeDelete 来管理一个类的成员指针,成员被很好地封装,拥有更多指向相同数据的指针的假设会失败。您想要访问的数据只能由对象检索,并且由于安全删除,容器对象在删除后的任何使用都可以很容易地被发现。在本地指针上使用 safe_delete,或仅在指向已删除数据的指针之一上使用没有意义。
      猜你喜欢
      • 1970-01-01
      • 2022-09-27
      • 2012-08-21
      • 2022-09-28
      • 2013-08-11
      • 1970-01-01
      • 1970-01-01
      • 2014-09-29
      • 2022-07-07
      相关资源
      最近更新 更多