【问题标题】:NULL check before deleting an object with an overloaded delete使用重载删除删除对象之前的 NULL 检查
【发布时间】:2011-04-18 18:53:59
【问题描述】:

这是代码审查 cmets 之一。

在为任何对象调用 delete 之前检查 NULL 是否是个好主意?

我确实理解 delete 运算符在内部检查 NULL 并且是多余的,但提出的参数是 delete,因为运算符可以重载,如果重载版本不检查 NULL,它可能会崩溃。那么假设删除是否以及何时重载是否会检查NULL是否安全合理? 在我的理解中,假设第一种情况是重载删除应该处理 NULL 检查是合理的,并且审查点并不成立。你怎么看?

【问题讨论】:

  • 哈。显然没有人(包括我在内)在回答之前完全阅读了这个问题。
  • @Konrad - :) 告诉我们人们有多么渴望提供帮助。这是一个很棒的网站。喜欢它!

标签: c++ null delete-operator


【解决方案1】:

不,不要检查空值。标准说delete (T*)0; 是有效的。它只会使您的代码复杂化而没有任何好处。如果operator delete 被重载,最好在运算符的实现中检查null。只是节省代码行和错误。

编辑:这个答案被接受和赞成,但在我看来,它的信息量不是很大。这里的所有答案都缺少一个,为了良心,让我在这里添加最后一个。

标准实际上在 [basic.stc.dynamic] 中说,至少从 C++03 开始​​:

在 C++ 程序中定义的任何分配和/或解除分配函数,包括库中的默认版本,都应符合 3.7.4.1 和 3.7.4.2 中指定的语义。

引用的部分以及其他答案中列出的标准中的其他一些地方说传递空指针的语义是无操作的。

【讨论】:

  • @Naveen @Als:是的。不,在“符合”这个词的强烈意义上。如果您实现不检查 NULL 的 operator delete,它仍然符合(例如,它只打印 cout << address << '\n')。但是,如果它带来未定义的行为,那是您的 operator delete 实现中的一个错误。
  • @David:请完整阅读 ybungalobill 的评论——如果它不调用 UB,它可以符合要求。例如:placement delete 不对其参数做任何事情(根本),所以它也不需要检查 null。
  • @David:所以,通过自定义new操作符,您可以(并且应该)定义相应的自定义delete操作符,这样如果对象的构造失败,分配的内存可以被释放.位置delete 对应于位置new(当然,除了返回给定的指针之外,它什么也不做)。
  • @David:是的,我并不是建议您自己放置delete,而是将其用作“必须检查 null 或不合格”语句的反例。
  • @David:正如@ybungalobill 所说,不需要检查 NULL 来触发 UB。因此,检查 NULL 不需要符合要求,只需要它与 NULL 一起工作。
【解决方案2】:

我会说这是确保如果您重载operator delete,那么您应该始终让它检查NULL,否则您会破坏语义。

【讨论】:

    【解决方案3】:

    在为任何对象调用 delete 之前检查 NULL 是否是个好主意?

    不!

    int *p = NULL;
    delete p ; //no effect
    

    标准说 [第 5.3.5/2 节]

    如果操作数具有类类型,则通过调用上述转换函数将操作数转换为指针类型,并在本节的其余部分中使用转换后的操作数代替原始操作数。在任一替代方案中,如果 delete 的操作数的值为空指针,则该操作无效

    18.4.1.1/13 部分的更多内容

    void operator delete(void* ptr) throw();

    void operator delete(void* ptr, const std::nothrow_t&) throw();

    默认行为:

    对于 ptr 的 null 值,什么也不做

    - ptr 的任何其他值应是调用默认操作符 new 之前返回的值,该值不会因对操作符 delete(void*) (17.4.3.7) 的介入调用而无效。对于 ptr 的这种非空值,回收先前调用默认运算符 new 分配的存储空间。

    编辑

    James Kanze here 这么说

    检查仍然是操作员delete(或delete[])的责任;该标准不保证不会给它一个空指针;如果给定一个空指针,该标准要求它是一个空操作。或者允许实现调用它。根据最新草案,“提供给释放函数的第一个参数的值可能是空指针值;如果是这样,并且释放函数是标准库中提供的函数,则调用无效。”我不太确定“是标准库中提供的”的含义是什么——从字面上看,因为他的功能不是标准库提供的,所以这句话似乎不适用.但不知何故,这没有任何意义。

    【讨论】:

    • @wilhelmtell : 你看不到一个很大的NO吗?
    • 我可以,但是您的推理显然表明您停止阅读问题的标题。 OP KNOWS 这是默认情况下的 noop。
    • @wihelmtell :不,我没有在阅读问题标题后停下来。这就是为什么我链接了bytes.com 线程并要求他阅读 James Kanze 的答案。
    【解决方案4】:

    我会说重载的delete 有责任表现得像你期望delete 的行为。也就是说,它应该将 NULL 指针作为无操作处理。

    因此,当调用重载删除时,您应该检查 NULL。您应该依靠重载的删除来正确实现。

    【讨论】:

      【解决方案5】:

      无需检查 null。 delete 运算符会检查 null,因此不需要额外检查。

      【讨论】:

        【解决方案6】:

        delete (T*)0; 有效且不执行任何操作,同样free(NULL); 也有效且不执行任何操作。如果您重载 delete 运算符,您的实现应该具有相同的语义。该标准说明了标准delete 将如何工作,但我不认为它说明了重载的delete 应该如何表现。为了与标准/默认行为保持一致,它应该允许 (T*)0 作为输入。

        【讨论】:

          【解决方案7】:

          来自标准文档,18.5.1.1.13delete 下,

          默认行为:如果 ptr 为 null,则不执行任何操作。 否则,回收先前调用分配的存储空间 新运算符。

          所以,您不必默认检查..

          【讨论】:

            【解决方案8】:

            删除前无需检查 NULL。如果有人让delete 的行为不符合标准,那么这就是真正的问题。任何人都不应轻视重载 delete 的任务,并且应始终支持预期的行为,例如检查 NULL 并且不采取任何措施。

            然而,无论如何,您应该始终记住将零分配给您刚刚删除的任何指针,除非您也将要删除该指针:

            void MyObj::reset()
            {
                delete impl_;
                impl_ = 0;    // Needed here - impl_ may be reused / referenced.
            }
            
            MyObj::~MyObj()
            {
                delete impl_; // No need to assign here as impl_ is going out of scope.
            }
            

            【讨论】:

              【解决方案9】:

              是否不需要检查。如果有人超载方法,是他的责任与NULL。

              【讨论】:

                【解决方案10】:

                我会说这些问题包含不完整的信息。我的商店在我们的编码标准中仍然在删除之前检查 NULL,因为我们仍然有一个我们必须支持的编译器/平台配置,如果它被传递为 NULL,则使用默认删除运算符进入未定义的行为。如果原贴有类似情况那么有一点要检查NULL,否则,改变编码标准!

                【讨论】:

                • 对不起,不,我的平台没有任何此类限制,但假设一个总是编写可移植代码删除实现将不得不在大多数时候重载(基于内存泄漏检测的日志记录,特殊内存实现等) 所以这个问题仍然成立不是吗?
                • @Als 编译器/平台配置将进入未定义的行为,即使是内置类型(即 int、char 等),因为它确实是平台 free() 实现的一个错误,但是有没有修复它,因为它是一个死平台。
                【解决方案11】:

                有点 C++ 学究气: NULL 不是一个内置的概念。是的,我们都知道它的含义,但在 C++0X 之前的 C++ 中,空指针概念就是值 0。NULL 通常是扩展为 0 的特定于平台的宏。

                使用 C++0X,我们得到了 nullptr,它比纯零更清晰,并且不能转换为除 bool 之外的任何整数类型,并且是比 NULL 更好的概念(或者可能是 NULL 背后概念的更好实现) .

                【讨论】:

                • 另外,很多 C++ 迂腐:空指针是值“空指针”,而不是(必然)值 0,它是整数,而不是指针。当转换为指针类型时,值为 0 的 constant 整数表达式会产生一个空指针(因此称为“空指针常量”),但这是标准中定义的一种特殊情况。
                • 好吧,因为通过 C++0X 之前的语言定义空指针的唯一方法是使用常量值文字 0,这会导致调用整数重载而不是指针重载,即真的很迂腐,-)
                • 还有其他获取空指针的方法。例如,std::strstr("a","b");。显然,使用空指针常量是明智的方式。我的额外迂腐只是指出0(一个整数,一个空指针常量,以及一个可能的NULL值)与(char*)0(void*)0(都是空指针,和空指针)之间存在差异常量,但对于 C++ 中的 NULL 都不合法)。
                猜你喜欢
                • 1970-01-01
                • 2023-03-30
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2012-09-26
                • 2011-07-17
                • 1970-01-01
                相关资源
                最近更新 更多