
David A. answered 10/10/19
Experienced C++ tutor who loves to mentor and pass on my knowledge
Short answer:
The new operator is not bad per-se; rather, in modern C++ there is a ‘better’, more robust option over the use of new, which are the use of smart pointers (i.e. std::unique_ptr, std::shared_ptr, std::weak_ptr). Hence the suggestion to ‘minimize’ or eliminate the need for new/delete.
Longer answer:
The canonical concern with using new is ensuring that for every call to new, there is a corresponding delete at some point. So, if you allocate memory using new, and forget to delete the memory (or an exception occurs before delete is called?), you will have a memory leak. In addition, if you tried to use a pointer that was already deleted, undefined behavior results.
In and of itself, this is pretty straightforward and valid C++ code; however, when dealing with a large project with potentially many developers working on a code base, the ownership of this pointer may become muddled; i.e:
- Who owns the resource of memory that was allocated?
- Who should be responsible for deleting/freeing the allocated memory?
- How do we ensure that only owners of the resource have control of the resource and allow others limited read-only viewable access?
This is where Modern C++ techniques enters and the addition of the smart pointer. Let’s take a look at one of the smart pointers that C++11 introduced: std::unique_ptr…
Notice there was no new or delete? The destructor for std::unique_ptr was automatically called at the end of the function and freed the memory used by the smart pointer (when ptr went out of scope).
There are a couple of observations to make:
- There was no new keyword used and no delete keyword used, yet memory was allocated for the integer and memory was released once the smart pointer fell out of scope.
- I didn’t have to remember to call delete — no memory leak (safer) :)
Now let’s look at a vector:
#include <iostream>
#include <vector>
void printInts(const int *rawPointer, size_t numElements) {
for (int i = 0; i < numElements; ++i) {
std::cout << *(rawPointer + i) << '\n';
}
//delete rawPointer; // Line #8 - Don't do this!
}
int main() {
std::vector vec = {1,2,3,4,5}; // memory auto allocated
printInts(vec.data(), vec.size());
}
vec.data() returns a raw pointer to its first element in the vector. Notice that I did not call delete on the raw pointer at the end of the printInts() function! The reason is because rawPointer does not own the memory that the vector object manages. If I were to uncomment line #8, you may receive an error, something like:
malloc: error: pointer being freed was not allocated
The owner of the allocated memory is the vector object. Although the vector object was kind enough to provide me with a pointer to its internal data structure (rawPointer), it does not give me ownership rights to delete or reallocate memory on its behalf.
Ownership is a big topic in Modern C++ , which is worthy of learning if this is new to you :)
Moral of the story:
- Always use smart pointers in place of raw pointers if ownership of resources (memory) is needed
- For all non-owning pointers to data, a raw pointer is fine to use
- Never call new or delete with raw pointers
- If you follow this general advice, you will have no memory leaks and a well-behaved program :)
I hope this helps!