【问题标题】:Will new return NULL in any case?new 在任何情况下都会返回 NULL 吗?
【发布时间】:2022-11-19 02:57:05
【问题描述】:

我知道根据 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

    8.0 (VS2005) 中的 MS cleaned this up 似乎总是默认为 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 - 至少在大多数情况下。

    【讨论】:

    • “代码完成”建议预先分配内存的“安全网”,以便在遇到内存不足的情况时使用,例如,可以在退出前保存调试信息。
    • 问题是在现代 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++标准头文件定义了一个空的struct 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。您也不能通过处理程序修改 new 的返回值。
    • 删除后将指针设置为 NULL 可能是一个好主意(对于 C)。但在 C++ 中,它是一种代码味道,表明 RAII 没有被正确使用。我认为这个建议已经过时了。
    • @Martin:不。只是......不。尝试在调试器中找出程序的状态,NULLed 指针是你的朋友。
    • 我并不是说这是一件坏事。只是它是一种代码味道。如果您有一个指针在删除后可能会被使用,则需要担心更大的设计问题。将 RAW 指针设置为 NULL 是一个警告标志;请问为什么这个指针还可以被滥用!
    猜你喜欢
    • 2010-10-07
    • 1970-01-01
    • 1970-01-01
    • 2020-02-07
    • 1970-01-01
    • 2021-08-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多