Nicolas
Nicolas

Reputation: 121

C++: When to use "new" and when not? | Add: Not duplicated

I'm new to C++ and wondering when you should use new and when not, e.g. "int x = 5" or "int * x = new int(5)". I know that new reserves memory in the heap and will thus not be deleted when the block ends, but since the variable the adress is saved in will become unaccessible beyond the block I don't see any benefits.

Example:

if(x) {
 int * z = new int(5);
 // Do something
}
// At this point the 5 is saved somewhere but since z is unaccessible I can't use it.

Add: This question is not duplicated since the other questions do only explain what is the heap while benefits of it are not described.

Upvotes: 1

Views: 210

Answers (3)

Richard Chambers
Richard Chambers

Reputation: 17613

The main characteristics of the C++ heap are that any memory allocated, using new will stay allocated until it is deallocated, using delete and that the heap can be used for large allocations of memory for a large data structure.

The downside of allocating memory with new is that the memory allocation must now be managed because the C++ standard does not have provisions for garbage collection currently (C++17). So if you allocate memory with new, it is your responsibility to make sure that delete is called at some point otherwise you may end up with a memory leak in which the amount of memory your application is using keeps growing larger and larger.

Local variables that are on the C++ stack will be automatically eliminated once the scope where the variable was created is exited. However since the C++ stack normally has much less memory available than the heap, it should not be used for large allocations of memory.

You use the same mechanisms and procedures and coding to make the variable z and the value of that variable available as long as you need it whether z is a pointer or z is not a pointer. The difference is that if you are using a pointer, you must release the memory it is pointing to before it goes out of scope or you must save the pointer value so that you can delete the allocated memory later.

Note that a basic pointer such as int *z that is pointing to a memory area will be eliminated when it goes out of scope just like any other variable however the memory to which it is pointing is not released unless you do an explicit delete before the pointer variable is no longer accessible.

Using variables on the C++ stack provide some benefits. Memory areas are automatically allocated each time the function is called and are automatically deallocated when the function exits. Multiple threads using the same function will each have their own stack space and their use of the function will result in each thread having its own set of stack allocated variables. Using the stack also allows for re-entrant code as well as recursive code so that issues dealing with interrupts and functions which call themselves work out well.

See the following posts for information about recursion and reentrancy.

Understanding how recursive functions work

What exactly is a reentrant function?

Recommended practices for re-entrant code in C, C++

However we come to the problem of data structure size and that some data structures need to have a life cycle which does not depend on one particular block or function. This is where heap allocation using new is useful and actually necessary.

There are several different approaches to using new and heap allocation. The best way is to let the compiler figure it out by using smart pointers. Smart pointers will release allocated memory as part of their destruction processing when the smart pointer variable goes out of scope.

For instance see:

What is meant by Resource Acquisition is Initialization (RAII)?

RAII and smart pointers in C++

Concerning your observation that allocated memory will become "inaccessible" when the pointer containing the address goes out of scope. You are correct and it is your responsibility to ensure that you release the memory with delete before the pointer containing the address goes out of scope. And this is the very problem that smart pointers were invented to solve. Another point is that there is no difference in accessing a variable in either of the following two examples:

if (x) {
    int *z = new(5);
    // .. do things with z or *z
    delete z;
}
// variable z is no longer available

or

if (x) {
    int z = 5;
    // .. do things with z
}
// z is no longer available

In both cases the variable z goes out of scope and can no longer be used. The only difference between using these two different versions of z is you have to dereference the pointer to access the value pointed to in the first version and in the second version you don't.

Upvotes: 1

Kaitain
Kaitain

Reputation: 960

Imagine that you are walking across town and have a number of different jobs to do at a number of different buildings. You have a bag that can carry notes in it. And every building also has an empty notepad that you can use, but you aren't allowed to take it out of the building.

It may be that you need to have certain notes that you take with you between jobs. These have to be written down and put inside your bag that goes with you from job to job across town. As you keep adding notes, the bag gets heavier, and it also takes time to put them into and take them out of the bag. (You can only take a note out of the bag briefly to consult it.) You can dump one of these notes at any time, but that also takes a bit of time (and then it's gone forever).

By contrast, some notes are only relevant to the local job/building. For these, it's best to use the building's own local notepad. You can't take these notes with you when you leave the building (the notepad is actually thrown in the trash when you leave), but it's quicker and easier to add and read notes from a building's notepad than it is to use the notes in your carry-everywhere bag (it ALWAYS takes more time getting those notes out of your bag than it does to consult the local notepad).

Sometimes you'll find you want to copy some of your local notes (on a notepad) into your permanent set of notes in your bag, because the information is needed for a later job you'll be doing. And sometimes it turns out to be efficient to copy some of your permanent notes onto the local building notepad when you first enter the building, because if you're going to be there for a while, consulting these notes frequently, it's actually easier to have them on the easy-to-read notepad than to take them out of your bag all the time (remember, you have to move them out of the bag EVERY time you read them).

The bag of permanent notes is the heap. Use it for things you'll want to take with you between jobs, but don't use it if you can use a local notepad just as easily. The local notepad is faster to use, and it doesn't clog up space in your bag. But that also means you can't use it for information you'll need to take between jobs, unless you copy that information into your bag of notes. The local notepad is the stack.

There are actually some complications to this: unknown to you, your permanent bag actually has clever layers inside it that mean that when you pull a note out of it from deep within the bag, it sits closer to the top of the bag for a while so that the next retrieval takes less time. But you can't count on that mechanism: in general, you should assume that it is expensive to take notes out of your bag.

Upvotes: 1

chili
chili

Reputation: 676

Use heap allocation when you:

  • Want an object to live past the end of the scope in which it was created
  • Require an array (block of memory) whose size is not known at build time
  • Are creating an object or memory block so large that it will cause this website's namesake (a stack overflow)

Consider the situation where you have a function make that creates an object and returns a pointer to it. If you allocate that object on the stack, it will not exist after the function ends. Allocated on the heap, it exists until the memory is freed with a call to delete, and therefore the caller can safely make use of the object created in make.

(I use the term 'safely' here loosely, since the responsibility for freeing the memory now falls on a different entity than the one that allocated it. Should the new owner of the memory neglect to free it, a memory leak would occur. The solution for this is to use smart pointers to manage your dynamically-allocated memory, specifically unique_ptr).

Upvotes: 5

Related Questions