When to Use New and Delete

About the usage of new and delete, and Stroustrup's advice

It's a great rule. In fact, you can avoid using new in arguments to smart pointers by using the appropriate make_ functions. For example, instead of:

std::shared_ptr<int> p(new int(5));

You can often do:

auto p = std::make_shared<int>(5);

This also has the benefit of being more exception safe. While a std::make_unique doesn't yet exist, it is planned to make its way into C++14 (it is already in the working draft). If you want it now, there are some existing implementations.

You can go a step further and even avoid using new and delete in constructors and destructors. If you always wrap dynamically allocated objects in smart pointers, even when they're class members, you won't need to manage your own memory at all. See the Rule of Zero. The idea is that it's not the responsibility of your class to implement any form of ownership semantics (SRP) - that's what the smart pointers are for. Then you theoretically never have to write copy/move constructors, copy/move assignment operators or destructors, because the implicitly defined functions will generally do the appropriate thing.

When to use new and when not to, in C++?

You should use new when you wish an object to remain in existence until you delete it. If you do not use new then the object will be destroyed when it goes out of scope. Some examples of this are:

void foo()
{
Point p = Point(0,0);
} // p is now destroyed.

for (...)
{
Point p = Point(0,0);
} // p is destroyed after each loop

Some people will say that the use of new decides whether your object is on the heap or the stack, but that is only true of variables declared within functions.

In the example below the location of 'p' will be where its containing object, Foo, is allocated. I prefer to call this 'in-place' allocation.

class Foo
{

Point p;
}; // p will be automatically destroyed when foo is.

Allocating (and freeing) objects with the use of new is far more expensive than if they are allocated in-place so its use should be restricted to where necessary.

A second example of when to allocate via new is for arrays. You cannot* change the size of an in-place or stack array at run-time so where you need an array of undetermined size it must be allocated via new.

E.g.

void foo(int size)
{
Point* pointArray = new Point[size];
...
delete [] pointArray;
}

(*pre-emptive nitpicking - yes, there are extensions that allow variable sized stack allocations).

Are new and delete still useful in C++14?

While smart pointers are preferable to raw pointers in many cases, there are still lots of use-cases for new/delete in C++14.

If you need to write anything that requires in-place construction, for example:

  • a memory pool
  • an allocator
  • a tagged variant
  • binary messages to a buffer

you will need to use placement new and, possibly, delete. No way around that.

For some containers that you want to write, you may want to use raw pointers for storage.

Even for the standard smart pointers, you will still need new if you want to use custom deleters since make_unique and make_shared don't allow for that.

Why should C++ programmers minimize use of 'new'?

There are two widely-used memory allocation techniques: automatic allocation and dynamic allocation. Commonly, there is a corresponding region of memory for each: the stack and the heap.

Stack

The stack always allocates memory in a sequential fashion. It can do so because it requires you to release the memory in the reverse order (First-In, Last-Out: FILO). This is the memory allocation technique for local variables in many programming languages. It is very, very fast because it requires minimal bookkeeping and the next address to allocate is implicit.

In C++, this is called automatic storage because the storage is claimed automatically at the end of scope. As soon as execution of current code block (delimited using {}) is completed, memory for all variables in that block is automatically collected. This is also the moment where destructors are invoked to clean up resources.

Heap

The heap allows for a more flexible memory allocation mode. Bookkeeping is more complex and allocation is slower. Because there is no implicit release point, you must release the memory manually, using delete or delete[] (free in C). However, the absence of an implicit release point is the key to the heap's flexibility.

Reasons to use dynamic allocation

Even if using the heap is slower and potentially leads to memory leaks or memory fragmentation, there are perfectly good use cases for dynamic allocation, as it's less limited.

Two key reasons to use dynamic allocation:

  • You don't know how much memory you need at compile time. For instance, when reading a text file into a string, you usually don't know what size the file has, so you can't decide how much memory to allocate until you run the program.

  • You want to allocate memory which will persist after leaving the current block. For instance, you may want to write a function string readfile(string path) that returns the contents of a file. In this case, even if the stack could hold the entire file contents, you could not return from a function and keep the allocated memory block.

Why dynamic allocation is often unnecessary

In C++ there's a neat construct called a destructor. This mechanism allows you to manage resources by aligning the lifetime of the resource with the lifetime of a variable. This technique is called RAII and is the distinguishing point of C++. It "wraps" resources into objects. std::string is a perfect example. This snippet:

int main ( int argc, char* argv[] )
{
std::string program(argv[0]);
}

actually allocates a variable amount of memory. The std::string object allocates memory using the heap and releases it in its destructor. In this case, you did not need to manually manage any resources and still got the benefits of dynamic memory allocation.

In particular, it implies that in this snippet:

int main ( int argc, char* argv[] )
{
std::string * program = new std::string(argv[0]); // Bad!
delete program;
}

there is unneeded dynamic memory allocation. The program requires more typing (!) and introduces the risk of forgetting to deallocate the memory. It does this with no apparent benefit.

Why you should use automatic storage as often as possible

Basically, the last paragraph sums it up. Using automatic storage as often as possible makes your programs:

  • faster to type;
  • faster when run;
  • less prone to memory/resource leaks.

Bonus points

In the referenced question, there are additional concerns. In particular, the following class:

class Line {
public:
Line();
~Line();
std::string* mString;
};

Line::Line() {
mString = new std::string("foo_bar");
}

Line::~Line() {
delete mString;
}

Is actually a lot more risky to use than the following one:

class Line {
public:
Line();
std::string mString;
};

Line::Line() {
mString = "foo_bar";
// note: there is a cleaner way to write this.
}

The reason is that std::string properly defines a copy constructor. Consider the following program:

int main ()
{
Line l1;
Line l2 = l1;
}

Using the original version, this program will likely crash, as it uses delete on the same string twice. Using the modified version, each Line instance will own its own string instance, each with its own memory and both will be released at the end of the program.

Other notes

Extensive use of RAII is considered a best practice in C++ because of all the reasons above. However, there is an additional benefit which is not immediately obvious. Basically, it's better than the sum of its parts. The whole mechanism composes. It scales.

If you use the Line class as a building block:

 class Table
{
Line borders[4];
};

Then

 int main ()
{
Table table;
}

allocates four std::string instances, four Line instances, one Table instance and all the string's contents and everything is freed automagically.

Are 'new' and 'delete' getting deprecated in C++?

Well, for starters, new/delete are not getting deprecated.

In your specific case, they're not the only solution, though. What you pick depends on what got hidden under your "do something with array" comment.

Your 2nd example uses a non-standard VLA extension which tries to fit the array on the stack. This has certain limitations - namely limited size and the inability to use this memory after the array goes out of scope. You can't move it out, it will "disappear" after the stack unwinds.

So if your only goal is to do a local computation and then throw the data away, it might actually work fine. However, a more robust approach would be to allocate the memory dynamically, preferrably with std::vector. That way you get the ability to create space for exactly as many elements as you need basing on a runtime value (which is what we're going for all along), but it will also clean itself up nicely, and you can move it out of this scope if you want to keep the memory in use for later.

Circling back to the beginning, vector will probably use new a few layers deeper, but you shouldn't be concerned with that, as the interface it presents is much superior. In that sense, using new and delete can be considered discouraged.

Use of new and delete in C++

The fist one allocates a single char. You delete it with:

delete a;

The second one allocates an array of chars. The length you have chosen is a little strange. You deallocate it with:

delete[] a;

Now... I hope you don't think you can put a stringified number in the second a (something like "123456", because you'll need many more bytes. Let's say at least 12 if an int is 32 bits. There is a funny formula to calculate the minimum length necessary. It's an approximation of the log10 https://stackoverflow.com/a/2338397/613130

To be clear, on my machine sizeof(int) == 4, but in an int I can put -2147483648 that is 10 digits plus the -, so 11 (plus the zero terminator)



Related Topics



Leave a reply



Submit