C++ course menu
C++ Dynamic Memory: Stack vs Heap, new and delete, Memory Leaks
Learn how C++ uses the stack and the heap, allocate memory with new and new[], free it with delete and delete[], and avoid memory leaks, double deletes and dangling pointers. Then see RAII, the idea that makes manual delete unnecessary.
On this page
So far, every variable has been created and destroyed automatically. Sometimes you need memory that lives longer than the function that creates it, or whose size you only know at run time. C++ lets you request that memory from the heap with new, and give it back with delete. Understanding this is essential, even though modern C++ code rarely writes delete by hand.
The stack and the heap#
A std::vector object itself sits on the stack, but the elements it holds live on the heap, which is why it can grow to millions of elements.
new and delete#
new Type(value) allocates one object on the heap and returns a pointer to it. delete pointer destroys the object and frees the memory:
#include <iostream>
int main() {
int* p = new int(42); // allocate an int on the heap
std::cout << *p << '\n';
*p += 8;
std::cout << *p << '\n';
delete p; // free it
p = nullptr; // avoid a dangling pointer
}42
50Every new must be matched by exactly one delete.
Arrays: new[] and delete[]#
To allocate an array whose size is known only at run time, use new[], and free it with delete[]:
#include <iostream>
int main() {
int n = 5;
int* squares = new int[n];
for (int i = 0; i < n; ++i) {
squares[i] = i * i;
}
for (int i = 0; i < n; ++i) {
std::cout << squares[i] << ' ';
}
std::cout << '\n';
delete[] squares; // [] because it was allocated with new[]
}0 1 4 9 16Mixing them up (delete on memory from new[]) is undefined behaviour. In practice, use std::vector<int> squares(n); instead: it does all of this for you.
What goes wrong with manual memory#
Manual new/delete is the source of a whole family of bugs:
Memory leak: forgetting to delete. The memory stays allocated until the program ends. In a long-running program (a server, a game), leaks pile up until it runs out of memory.
void leak() {
int* data = new int[1000];
// ... return without delete[] data; 4000 bytes lost every call
}Leak on early exit: even careful code leaks if it returns early, or if an exception is thrown, between new and delete:
void process() {
int* buffer = new int[100];
if (!load(buffer)) {
return; // leak: delete[] is never reached
}
delete[] buffer;
}Dangling pointer / use after free: using memory after it has been deleted.
Double delete: deleting the same memory twice. Both of these are undefined behaviour and can crash or corrupt data far away from the real bug.
Tools like AddressSanitizer (-fsanitize=address in GCC and Clang) and Valgrind find these bugs, but it is far better not to create them.
RAII: let objects own memory#
The C++ solution is RAII ("Resource Acquisition Is Initialization"): wrap each resource in an object whose constructor acquires it and whose destructor releases it. Because destructors run automatically when an object goes out of scope (even on early return or an exception), the resource can never leak.
Here is a tiny RAII class that owns a heap array (you will learn the class syntax in Classes and Constructors and Destructors):
#include <iostream>
class IntBuffer {
public:
explicit IntBuffer(int size) : data_(new int[size]{}), size_(size) {
std::cout << "allocated " << size_ << '\n';
}
~IntBuffer() {
delete[] data_;
std::cout << "freed " << size_ << '\n';
}
IntBuffer(const IntBuffer&) = delete; // no accidental copies
IntBuffer& operator=(const IntBuffer&) = delete;
int& operator[](int i) { return data_[i]; }
private:
int* data_;
int size_;
};
int main() {
{
IntBuffer buf(3);
buf[0] = 7;
std::cout << "using " << buf[0] << '\n';
} // buf's destructor runs here, automatically
std::cout << "after the block\n";
}allocated 3
using 7
freed 3
after the blockYou will rarely write a class like this yourself, because the standard library already provides RAII owners for every common need:
The rule of modern C++: no naked new and delete in everyday code. See Smart Pointers.
Common mistakes#
Practice#
Practice exercise
This function leaks memory when n is negative. Rewrite it with std::vector so that it cannot leak, and keep the same output for n = 4.
int sum_of_squares(int n) {
int* values = new int[n > 0 ? n : 1];
if (n < 0) return -1; // leaks!
int total = 0;
for (int i = 0; i < n; ++i) {
values[i] = (i + 1) * (i + 1);
total += values[i];
}
delete[] values;
return total;
}Show solution
#include <iostream>
#include <vector>
int sum_of_squares(int n) {
if (n < 0) return -1;
std::vector<int> values(n);
int total = 0;
for (int i = 0; i < n; ++i) {
values[i] = (i + 1) * (i + 1);
total += values[i];
}
return total; // the vector frees its memory automatically
}
int main() {
std::cout << sum_of_squares(4) << ' ' << sum_of_squares(-2) << '\n';
}30 -1Key takeaway#
Finished this lesson?