为什么我们需要在 C 中检查 malloc 的 return 值,而不是在 C++ 中检查 new?

Why do we need to check the return value of malloc in C, but not new in C++?

C++ 中有一点我不明白。相比之下,在 C 中,当我们调用 malloc 时,我们检查分配是否失败,然后释放所有先前分配的内存,以便在没有内存泄漏的情况下干净退出。

但在 C++ 中,似乎没有人关心 new 运算符。我知道它会在失败时抛出 std::bad_alloc,但是如果之前有成功的 new 分配,我们会在抛出异常时留下内存泄漏,不是吗?

在我的例子中,内存泄漏仍然存在,但它可能是一些明确丢​​失的内存。

int main(void) {
    int *ptr1 = new int;
    int *ptr2 = new int;

    // do something with ptrs
    delete ptr1;
    delete ptr2;
    return (EXIT_SUCCESS);
}

而在 C 中我们会这样做

#include <stdlib.h>

int main(void) {
    void *ptr1;
    void *ptr2;

    if (!(ptr1 = malloc(10)))
        return (EXIT_FAILURE);

    if (!(ptr2 = malloc(10))) {
        free(ptr1);
        return (EXIT_FAILURE);
    }
    //do something with ptrs
    free(ptr1);
    free(ptr2);
    return (EXIT_SUCCESS);
}

既然可以抛出 std::bad_alloc,那么我们不应该做这样的事情吗?

#include <new>

int main(void) {
    int *ptr1 = new int;
    int *ptr2;

    try {
        ptr2 = new int;
    } catch (std::bad_alloc &e) {
        // do something with exception
        delete ptr1;
        return (EXIT_FAILURE);
    }
    // do something with ptrs
    delete ptr1;
    delete ptr2;
    return (EXIT_SUCCESS);
}

new/delete是一个C++运算符,malloc/free是一个C/C++标准库函数

malloc/free只动态分配内存space/releasesspace,而new/delete除了分配space外还会调用构造函数和析构函数进行初始化和清理up(清理成员)

malloc/free需要手动计算字号,return值为void*,new/delete可以自己计算字号,return对应的字号指针

malloc/free 失败并 returns 0,并且 new/delete 失败并抛出异常。

But in C++ no one seems to care about that with the new operator. I know that it throws std::bad_alloc on failure, but if there were previous successful allocations with new we are left with memory leaks upon a thrown exception, aren't we?

只有当你正在编写非常糟糕 C++ 时,在这种情况下你应该停止它。

这个,例如:

{
    int *ptr1 = new int;
    int *ptr2 = new int;
    // do something with ptrs
    delete ptr1;
    delete ptr2;
}

是糟糕的风格。没有信誉良好的参考资料会建议您写这篇文章。 它本质上是 C 不同的关键字,在 C 中添加异常并不是一种改进。

So shouldn't we do something like this instead ...

不行,下面的不好(虽然更像Java用C++写的,而不是用C++写的C)

{
    int *ptr1 = new int;
    int *ptr2;

    try {
        ptr2 = new int;
    } catch (std::bad_alloc &e) {
         delete ptr1;
         return;
    }
    // do something with ptrs
    delete ptr1;
    delete ptr2;
}

通常的建议(例如,根据 Core Guidelines)是:

  1. 除非你真的需要,否则一开始不要使用动态分配

    (当然,我们会假设您只是简化了确实需要 new/delete 的东西,但显示的代码应该只使用自动局部变量)

  2. 如果必须动态分配,请使用 RAII 并且永远不要拥有原始指针

    所以你的原始代码的正确版本是

    {
        auto ptr1 = std::make_unique<int>();
        auto ptr2 = std::make_unique<int>();
        // do something with ptrs
    }
    

    实际上不那么复杂,而且完全正确。