Must New Always Be Followed by Delete

Must new always be followed by delete?

There is nothing that requires a delete[] in the standard - However, I would say it is a very good guideline to follow.

However, it is better practice to use a delete or delete[] with every new or new[] operation, even if the memory will be cleaned up by the program termination.

Many custom objects will have a destructor that does other logic than just cleaning up the memory. Using delete guarantees the destruction in these cases.

Also, if you ever move around your routines, you are less likely to cause memory leaks in other places in your code.

Why is there a special new and delete for arrays?

Objects created with new[] must use delete[]. Using delete is undefined on arrays.

With malloc and free you have a more simple situation. There is only 1 function that frees the data you allocate, there is no concept of a destructor being called either. The confusion just comes in because delete[] and delete look similar. Actually they are 2 completely different functions.

Using delete won't call the correct function to delete the memory. It should call delete[](void*) but instead it calls delete(void*). For this reason you can't rely on using delete for memory allocated with new[]

See this C++ FAQ

[16.13] Can I drop the [] when
deleteing array of some built-in type
(char, int, etc)?

No!

Sometimes programmers think that the
[] in the delete[] p only exists so
the compiler will call the appropriate
destructors for all elements in the
array. Because of this reasoning, they
assume that an array of some built-in
type such as char or int can be
deleted without the []. E.g., they
assume the following is valid code:

void userCode(int n)  {
char* p = new char[n];
...
delete p; // ← ERROR! Should be delete[] p !
}

But the above code is wrong, and it
can cause a disaster at runtime. In
particular, the code that's called for
delete p is operator delete(void*),
but the code that's called for
delete[] p is operator
delete[](void*)
. The default behavior
for the latter is to call the former,
but users are allowed to replace the
latter with a different behavior (in
which case they would normally also
replace the corresponding new code in
operator new[](size_t)). If they
replaced the delete[] code so it
wasn't compatible with the delete
code, and you called the wrong one
(i.e., if you said delete p rather
than delete[] p), you could end up
with a disaster at runtime.

Why does delete[] exist in the first place?

Whether you do x or y:

 char * x = new char[100]; 
char * y = new char;

Both are stored in char * typed variables.

I think the reason for the decision of delete, and delete[] goes along with a long list of decisions that are in favor of efficiency in C++. It is so that there is no enforced price to do a lookup of how much needs to be deleted for a normal delete operation.

Having 2 new and new[] seems only logical to have delete and delete[] anyway for symmetry.

How could pairing new[] with delete possibly lead to memory leak only?

Suppose I'm a C++ compiler, and I implement my memory management like this: I prepend every block of reserved memory with the size of the memory, in bytes. Something like this;

| size | data ... |
^
pointer returned by new and new[]

Note that, in terms of memory allocation, there is no difference between new and new[]: both just allocate a block of memory of a certain size.

Now how will delete[] know the size of the array, in order to call the right number of destructors? Simply divide the size of the memory block by sizeof(T), where T is the type of elements of the array.

Now suppose I implement delete as simply one call to the destructor, followed by the freeing of the size bytes, then the destructors of the subsequent elements will never be called. This results in leaking resources allocated by the subsequent elements. Yet, because I do free size bytes (not sizeof(T) bytes), no heap corruption occurs.

What does delete command really do for memory, for pointers in C++?

Think of memory as a big warehouse with lots of boxes to put things into. When you call "new", the warehouse staff finds an unused box large enough for your needs, records that box as being owned by you (so it's not given to someone else), and gives you the number of that box so you can put your stuff into it. This number would be the "pointer".

Now, when you "delete" that pointer, the reverse happens: the warehouse staff notes that this particular box is available again. Contrary to real warehouse staff they aren't doing anything with the box — so if you look into it after a "delete" you might see your old stuff. Or you might see somebody else’s stuff, if the box was reassigned in the meantime.

Technically, you are not allowed to look into your box once you have returned it to the pool, but this is a somewhat weird warehouse with no keys or guards, so you can still do whatever you want. However, it might cause problems with the new owner of the box, so it's expected that you follow the rules.

If I write operators new and delete for a class, do I have to write all of their overloads?

No, you don't need to write all variations of the new and delete operators for your class.

There are multiple reasons to prefer some versions of new and delete over others. I will describe each reason separately.

Almost always prefer the delete operators that have a size parameter over those without one.

When I write delete operators for a base class that provides memory handling for other classes, I use these versions of the delete operators

void T::operator delete  ( void* ptr, std::size_t sz );
void T::operator delete[]( void* ptr, std::size_t sz );
void T::operator delete ( void* ptr, std::size_t sz, std::align_val_t al ); // (since C++17)
void T::operator delete[]( void* ptr, std::size_t sz, std::align_val_t al ); // (since C++17)

and deliberately omit or =delete these versions.

void T::operator delete  ( void* ptr );
void T::operator delete[]( void* ptr );
void T::operator delete ( void* ptr, std::align_val_t al ); // (since C++17)
void T::operator delete[]( void* ptr, std::align_val_t al ); // (since C++17)

The reason is that the std::size_t sz parameter tells me the size of the object or size of the array. I can't know the sizes of the derived class' objects when I write my base class, so using the size parameter helps. Some of my memory handlers segregate the objects by size (easier to pool memory when all the chunks are the same size). I can use the size parameter to quickly choose which memory pool to search, rather than searching all of them. That turns a O(n) algorithm into a O(1) action.

Some of my memory allocators use a "chain model" instead of a "block model", and the size parameter helps for deleting there too. (I call a memory allocator a "block model" if it preallocates a huge chunk and then partitions the chunk into separate blocks like an array. I call a memory handler a "chain model" if each chunk points to previous and next chunks like a linked list or chain.) So when somebody deletes a chunk from a chain of memory chunks, I want the delete operator to know the chunk being deleted is the correct size. I can put an assertion in the delete operation that asserts (size == address of next chunk - address of this chunk).

Where appropriate, prefer the new and delete operators with the alignment parameter.

Now that C++17 provides an alignment parameter for new operators, use them if you need them. If you need performance, align your objects on 4, 8, or 16 byte boundaries, do so! It makes the program a little faster.

So let's say you have an alignment-aware memory allocator. It knows that some objects are best stored on 4 byte boundaries because those objects are small and you can squeeze more into memory if you use 4 byte boundaries. It also knows some objects are best aligned on 8 byte boundaries because those objects are used often.

Your memory handler will know this if it provides the correct new operators and derived classes provide the correct values for alignments.

The 2017 C++ Standard says:

When allocating objects and arrays of objects whose alignment exceeds STDCPP_DEFAULT_NEW_ALIGNMENT, overload resolution is performed twice: first, for alignment-aware function signatures, then for alignment-unaware function signatures. This means that if a class with extended alignment has an alignment-unaware class-specific allocation function, it is the function that will be called, not the global alignment-aware allocation function. This is intentional: the class member is expected to know best how to handle that class.

This means the compiler will check for new and delete operators with the alignment parameter, and then check for operators without the alignment parameter.

If you have an alignment-aware memory handler, then always provide these new operators, even if you also want to give your client code the option of ignoring alignment.

void* T::operator new  ( std::size_t count, std::align_val_t al ); // (since C++17)
void* T::operator new[]( std::size_t count, std::align_val_t al ); // (since C++17)
void* T::operator new ( std::size_t count,
std::align_val_t al, user-defined-args... ); // (since C++17)
void* T::operator new[]( std::size_t count,
std::align_val_t al, user-defined-args... ); // (since C++17)

You can force code to provide the alignment parameter if you provide the above new operators and omit or =delete these overloads.

void* T::operator new  ( std::size_t count );
void* T::operator new[]( std::size_t count );

void* T::operator new ( std::size_t count, user-defined-args... );
void* T::operator new[]( std::size_t count, user-defined-args... );

Use class specific placement-new operators to provide hints.

Let's say you wrote a class that allocates several data members, and you want all those data members to be located on the same memory page. If the data is spread across several memory pages, the CPU will have to load different memory pages into the L1 or L2 cache just so you can access the member data for an object. If your memory handler can place all of an object's data members onto the same page, then your program will run faster because the CPU will not need to load multiple pages into cache.

These are the class specific placement new operators.

void* T::operator new  ( std::size_t count, user-defined-args... );
void* T::operator new[]( std::size_t count, user-defined-args... );
void* T::operator new ( std::size_t count,
std::align_val_t al, user-defined-args... ); // (since C++17)
void* T::operator new[]( std::size_t count,
std::align_val_t al, user-defined-args... ); // (since C++17)

Overload them to look like this by providing a hint parameter.

void* T::operator new  ( std::size_t count, void* hint );
void* T::operator new[]( std::size_t count, void* hint );
void* T::operator new ( std::size_t count, std::align_val_t al, void* hint ); // (since C++17)
void* T::operator new[]( std::size_t count, std::align_val_t al, void* hint ); // (since C++17)

The hint parameter tells the memory handler to try to place the object not at the location of that hint address, but on the same page as the hint address.

Now you can write a class that looks like this which is derived from your memory handling class.

class Foo : public MemoryHandler
{
public:
Foo();
...
private:
Blah * b_;
Wham * f_;
};

Foo::Foo() : b_( nullptr ), f_( nullptr )
{
// This should put data members on the same memory page as this Foo object.
b_ = new ( this ) Blah;
f_ = new ( this ) Wham;
}

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).

Why no delete after new in linked list insert node

The code you cited should delete the nodes at some point. Indeed, that code shows off tons of bad C++ practices. It doesn't delete the nodes because it's bad code.

Oh and BTW: ignore anything on the site you linked to. If there is something useful on that site, it's only by accident.



Related Topics



Leave a reply



Submit