`New` and `Delete`

Interactive Graph

Table of Contents

new & delete

Before we go ahead and figure out what smart pointers are, let’s take a moment to look back to how we handle heap allocated memory in C++. Back in C, we had the malloc and free functions to handle heap memory. new and delete are C++ operators that try to do the same task, but cleaner.

int *b = (int*) malloc(sizeof(int)); // Old C-style heap allocation
int *c = new int; // New C++ heap allocation

malloc returns void*. You will notice that back in C, we did not have to explicitly cast a void* to int*, however in C++, implicit pointer type conversion is a compile error. int *b = malloc(sizeof(int)) will throw:

error: invalid conversion from ‘void*’ to ‘int*’ [-fpermissive]

In C++, new and delete are operators. They are not functions like their C counterparts. This means that just like any other operator, they can be overloaded to do pretty much anything. This means that the behaviour of new & delete are dependent on the C++ library and compiler that you are using.

However, most implementations just implement calling new to call the underlying malloc function. And similarly free for delete. But one key additional task that new does is it will also call the constructor for the object it is creating. And delete the destructor.

We’ll use the following Entity class as a toy-example when playing around with new / delete.

class Entity{
public:
	Entity(){
 		std::cout << "Constructor!" << std::endl; }
	Entity(const std::string &name) : name(name) {
		std::cout << "P-Constructor!" << std::endl; }
	~Entity() {
 		std::cout << "Destructor!" << std::endl; }
private:
	std::string name;
};

Here are three ways to use new in C++.

int main(void){
	Entity *obj = new Entity; // Output: Constructor!
	Entity *same_thing = new Entity(); // Output: Constructor!
	Entity *pobj = new Entity("abcd"); // Output: P-Constructor!, name: abcd
	Entity *obj_arr = new Entity[5];
	/**
	 * Output:
	 * Constructor!
	 * Constructor!
	 * Constructor!
	 * Constructor!
	 * Constructor!
	 */
	// This is called "placement new"
	std::cout << sizeof(Entity) << std::endl; // Output: 32
	int *space = new int[10];
	Entity *placement_new = new(space) Entity[2];
	/*
	 * Output:
	 * Constructor!
	 * Constructor!
	 */
}

So the first 4 examples are the basic ones. You’ll notice that new always makes it a point to call the constructor of the class we’re allocating memory for. This is an attempt to work around the uninitialized memory problem we have with malloc. Links back to RAII - Resource Acquisition Is Initialization principles as well. We don’t want uninitialized memory. We can use new to also initialize an object with it’s parameterized constructor instead of the default one. However we can’t parameter initialize an array of them :)

Getting a pointer to an array of Entity objects is also quite simple. obj_arr is a pointer to a contiguous chunk of memory that points to an array of 5 Entity objects.

The interesting new use-case here is the “placement new”. Here, new isn’t actually allocating a block of memory. It simply uses the previously allocated memory for space and just initializes Entity in that memory by calling it’s constructor.

For delete, it’s pretty similar.

delete obj; // Output: Destructor!
delete[] obj_arr;
/**
 * Output:
 * Destructor!
 * Destructor!
 * Destructor!
 * Destructor!
 * Destructor!
*/
// Note! It's also possible to compile
delete obj_arr; // Output: Destructor!

You’ll notice the last way to call delete actually just calls the destructor once. So when de-allocating a pointer to an array of elements in memory it’s important to always remember to use delete[] instead of delete to properly clean this memory.

Why new & delete?

One, it’s a lot cleaner than the C-style way. Two, it is a paradigm that avoids the uninitialized memory issue we can have when using the C-style malloc and free functions. new and delete prevent this from ever happening by always calling the constructor and destructor.

However, a problem they still don’t solve is the problem of memory leaks and dangling pointers. To solve this, we have the idea of Smart Pointers.