【问题标题】:Will new return NULL in any case?在任何情况下 new 都会返回 NULL 吗?
【发布时间】:2010-10-07 17:15:08
【问题描述】:

我知道根据 C++ 标准,如果 new 分配内存失败,它应该抛出 std::bad_alloc 异常。但我听说一些编译器,如 VC6(或 CRT 实现?)不遵守它。这是真的 ?我问这个是因为在每个新语句之后检查 NULL 会使代码看起来非常难看。

【问题讨论】:

    标签: c++ visual-c++ memory-management new-operator visual-c++-6


    【解决方案1】:

    VC6 在这方面默认是不合规的。 VC6 的new 返回0(或NULL)。

    这里是 Microsoft 关于此问题的知识库文章以及他们建议的使用自定义 new 处理程序的解决方法:

    如果您有为 VC6 行为编写的旧代码,则可以通过链接到名为 nothrownew.obj 的目标文件来使用较新的 MSVC 编译器(类似于 7.0 及更高版本)获得相同的行为。在 7.0 和 7.1 编译器(VS2002 和 VS2003)中实际上有一个 fairly complicated set of rules 来确定它们是默认为不抛出还是抛出 new

    似乎 MS cleaned this up 在 8.0 (VS2005) 中 - 现在它总是默认为 throwing new 除非您特别链接到 nothrownew.obj

    请注意,您可以使用std::nothrow 参数指定您希望new 返回0 而不是抛出std::bad_alloc

    SomeType *p = new(std::nothrow) SomeType;
    

    这似乎在 VC6 中有效,因此它可能是一种或多或少机械地修复代码以与所有编译器相同的方法,因此您不必重新处理现有的错误处理。

    【讨论】:

    • 错误的版本号。它在 5.0 中被破坏(正如您链接到的文章所说)。它已在 6.0 中修复。
    • VC6 默认也返回 NULL - 我刚刚测试过。根据“kftdy56f”链接,VC7 和 VC7.1(VS2002 和 VS2003)中的行为也可能返回 NULL,具体取决于是否链接了 libc*.lib 或 libcp*.lib(CRT 或 C++ 标准库) . 我没有兴趣测试它。
    • 公平地说,VC6 是在 C++ 标准被批准之前发布的,这也是它如此不符合标准的原因之一。确实,当时该标准已接近完成,但必须记住,存在开发周期,而且 VC6 可能至少早一年开始。
    【解决方案2】:

    我想补充一个(有点争议的)观点,即在分配尝试后检查 NULL 几乎是徒劳的。如果您的程序遇到这种情况,您可能只能快速退出。任何后续的分配尝试很可能也会失败。

    如果不检查 NULL,您的后续代码将尝试取消引用 NULL 指针,这往往会以相对独特(且易于调试)的退出条件快速退出程序。

    我并不是要劝你不要检查 NULL,这肯定是一种尽责的编程。但是您不会从中获得太多收益,除非在非常特殊的情况下,您可能可以存储一些恢复信息(不分配更多内存),或者释放不太重要的内存等。但对于大多数人来说,这些情况相对较少。

    鉴于此,我个人相信编译器会抛出 bad_alloc - 至少在大多数情况下。

    【讨论】:

    • "Code Complete" 建议预先分配内存的“安全网”,以便在遇到内存不足的情况时使用,以便在退出之前保存调试信息,例如例子。
    • 问题在于,在现代 VM 系统上,如果您来到任何附近耗尽(虚拟)内存的地方,该东西将分页太多以至于完全无法使用。
    • 在某些情况下,您的操作系统会让您分配内存而无需真正映射新页面(延迟评估)。但是,当您尝试使用该内存时,没有可用的内存,并且进程被杀死。便宜的硬盘驱动器和大型交换文件的问题更少......
    • 我不同意;有时无法分配内存不是终端,并且崩溃是不可取的。可能不需要处理每条数据,但如果跳过某些数据,则提醒操作员很重要。也不是每个人都有带有磁盘支持的内存管理环境。
    • @sharptooth, @Adam Hawes:您正在讨论分配内存是可选的情况 - 如果可以,您将使用它做一些事情。当然你需要检查 NULL 然后。在大多数情况下,内存是必不可少的,因此分配失败意味着整体失败。
    【解决方案3】:

    根据 C++ 规范,当你只使用没有参数的普通 new 时,它总是会抛出 std::bad_alloc,但当然也可能有一些不兼容的编译器。

    不过,我不会编写代码来与非 c++ 兼容的编译器兼容。 VC6 在这方面就是其中之一。

    虽然在删除它们后始终将指针设置为 NULL 是一种很好的做法。因此,仍然需要检查 NULL。

    话虽如此,这里有几个清理代码的选项:

    选项 1:设置您自己的新处理程序

    清理代码的安全方法是首先调用:set_new_handler

    然后您可以在处理程序中检查 NULL,如果返回 NULL,则将 std::bad_alloc 扔在那里。

    如果您更喜欢异常,那么这是您最好的选择。如果您希望更好地返回 NULL,那么您也可以通过在新处理程序中执行 catch 来做到这一点。

    选项 2:使用重载的 new

    c++ 标准头文件定义了一个空结构体 nothrow。您可以在 new 中使用此结构的对象来获取其始终返回 NULL 的重载版本。

    void* operator new (size_t size, const std::nothrow_t &);
    void* operator new[] (void *v, const std::nothrow_t &nt);
    

    所以在你的代码中:

     char *p = new(std::nothrow) char[1024];
    

    这里是a good refrence for further reading

    【讨论】:

    • 我了解删除后设置 NULL。但我的问题是这样的代码: int *p = new int; if( p == NULL) { // 记录内存分配失败.. return; }
    • 你可以在你的新处理程序中抛出 bad_alloc,但没有任何东西可以检查 NULL。你也不能通过handler修改new的返回值。
    • 在删除后将指针设置为 NULL 可能是一个好主意(对于 C 语言)。但是在 C++ 中,这是一种代码味道,表明 RAII 没有被正确使用。我认为这个建议已经过时了。
    • @Martin:不。只是……不。尝试在调试器中找出程序的状态,NULL 指针是你的朋友。
    • 我并不是说这是一件坏事。只是它是一种代码气味。如果您有一个可能在删除后使用的指针,则需要担心更大的设计问题。将 RAW 指针设置为 NULL 是一个警告信号;问问为什么这个指针仍然可以被滥用!
    猜你喜欢
    • 2022-11-19
    • 1970-01-01
    • 1970-01-01
    • 2020-02-07
    • 1970-01-01
    • 2021-08-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多