1

There is a thing that I don't understand in C++. To contrast, in C when we call malloc we check if the allocation failed to then free all the previously allocated memory so as to make a clean exit without memory leaks.

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?

In my example the memory leaks are still reachable, but it could be some definitely lost memory.

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

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

Whereas in C we'd do

#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);
}

So shouldn't we do something like this instead given std::bad_alloc can be thrown?

#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);
}
east1000
  • 1,240
  • 1
  • 10
  • 30
Fayeure
  • 1,181
  • 9
  • 21
  • Googling something like "c++ check return of new" might find https://stackoverflow.com/questions/239302/is-it-useful-to-test-the-return-of-new-in-c or https://stackoverflow.com/questions/550451/will-new-return-null-in-any-case or https://stackoverflow.com/questions/61850237/c-new-vs-malloc-return-value Take your pick for an answer. – TheUndeadFish Sep 15 '21 at 00:18
  • I know all that, you think I didn't search before? My question was why is it important in C but not in C++, as we get the same memory leaks – Fayeure Sep 15 '21 at 00:39
  • 1
    If you have a bunch on `new`s before the one that fails you will have to do similar clean-up to release the storage allocated by the other `new`s. But in C++ there are many alternatives to both `malloc` and `new`, and even if you are forced by rare circumstances to use `new` you should be immediately protecting the allocation with a [RAII wrapper](https://stackoverflow.com/questions/2321511/what-is-meant-by-resource-acquisition-is-initialization-raii) that will ensure proper handling no matter what happens in the future. – user4581301 Sep 15 '21 at 00:55
  • Take-away: Manage every resource as close to the resource as possible and avoid managing more than one resource like the plague. – user4581301 Sep 15 '21 at 00:57
  • Handy reading: [Why should C++ programmers minimize use of 'new'?](https://stackoverflow.com/questions/6500313/why-should-c-programmers-minimize-use-of-new) – user4581301 Sep 15 '21 at 01:08
  • 1
    Apologies if I misunderstood. Many people don't search first. If the response to a `new` failure is to end up exiting in the program anyway, then cleaning up memory is not really important. Generally speaking the OS is going to release the whole program's memory when it ends no matter what. (Though that answer could differ for OSs that aren't a modern desktop OS.) So it's easy to just let the `bad_alloc` exception propagate out until it closes the program. – TheUndeadFish Sep 15 '21 at 01:28
  • If you use raw owning pointers for this you *should* beware of a leak in these situation. The first example is just lazy. But it's the kind of code that only bad programmers write so it isn't surprising it is usually lazy. In reality you should use `std::unique_ptr` here which cleans everything up automatically if there is an exception. – François Andrieux Sep 15 '21 at 03:21

2 Answers2

4

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?

Only if you're writing very bad C++, in which case you should stop that.

This, for example:

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

is terrible style. No reputable reference would suggest you write this. It is essentially C with different keywords, and adding exceptions to C is not an improvement.

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

No, the following is also bad (although it is more like Java written in C++ than C written in 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;
}

The usual recommendations (for example, per the Core Guidelines) are:

  1. don't use dynamic allocation in the first place unless you really need it

    (of course we'll assume you're just simplifying something that really did need new/delete, but the code as shown should just use automatic locals instead)

  2. if you must have dynamic allocation, use RAII and never owning raw pointers

    so the correct version of your original code is

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

    which is actually less complex, as well as being perfectly correct.

Useless
  • 64,155
  • 6
  • 88
  • 132
  • 2
    It should be noted that there is a staggeringly large amount of bad C++ out there. There are even courses on how to write it taught in major universities. – user4581301 Sep 15 '21 at 17:30
1

new/delete is a C++ operator, malloc/free is a C/C++ standard library function

malloc/free only dynamically allocates memory space/releases space, while new/delete in addition to allocating space also calls the constructor and destructor to initialize and clean up (clean up members)

malloc/free needs to manually calculate the type size and the return value is void*, new/delete can calculate the type size by itself and return the corresponding type pointer

malloc/free fails and returns 0, and new/delete fails and an exception is thrown.

HackerDev-Felix
  • 226
  • 2
  • 5