Mastering New and delete in C++ gives control over memory lifetimes and sizes at runtime. This article explains single-object and array allocation, error handling, best practices, and safe patterns— with clear examples and challenges to build confidence.
🔹 Why Dynamic Memory (Runtime Allocation)?
Dynamic allocation is used when the size or lifetime of data isn’t known at compile time—e.g., reading N items from input, or managing objects that outlive the current scope.
new
allocates memory and constructs objects; delete
destroys and frees them. For arrays, use new[]
and delete[]
.
1. new/delete for a Single Object
Allocate with new T(...)
and free with delete
. Always set pointers to nullptr
after deletion to avoid dangling pointers.
#include <iostream>
int main() {
int* p = new int(42); // allocate and initialize
std::cout << "*p: " << *p << "\n";
delete p; // free
p = nullptr; // avoid dangling
}
Output
*p: 42
📝 Try it Yourself: Dynamically allocate a
double
, initialize it to3.14
, print it, then delete it and set the pointer tonullptr
.
2. new[] / delete[] for Arrays
Arrays require new[]
and must be freed with delete[]
. Never mix new
with delete[]
or new[]
with delete
.
#include <iostream>
int main() {
int n = 5;
int* arr = new int[n]{1,2,3,4,5}; // allocate array
for (int i = 0; i < n; ++i) std::cout << arr[i] << " ";
std::cout << "\n";
delete[] arr; // correct matching delete[]
arr = nullptr;
}
Output
1 2 3 4 5
📝 Try it Yourself: Read
n
from input, allocate an array ofn
ints, fill it withi*i
, print values, then free it withdelete[]
.
3. Handling Allocation Failure (std::bad_alloc)
By default, new
throws std::bad_alloc
if allocation fails. Use try/catch
to handle errors gracefully. Alternatively, use nothrow
to get nullptr
instead of an exception.
3.1 Exception form (throws std::bad_alloc)
#include <iostream>
#include <new>
int main() {
try {
std::size_t big = static_cast<std::size_t>(1) << 40;
int* huge = new int[big];
delete[] huge;
} catch (const std::bad_alloc&) {
std::cout << "Allocation failed!\n";
}
}
Output (example)
Allocation failed!
3.2 Nothrow form (returns nullptr)
#include <iostream>
#include <new>
int main() {
std::size_t big = static_cast<std::size_t>(1) << 40;
int* huge = new (std::nothrow) int[big];
if (!huge) {
std::cout << "Allocation returned nullptr\n";
return 0;
}
delete[] huge;
}
📝 Try it Yourself: Switch between exception and nothrow forms; log which path you hit and verify you always free memory when allocation succeeds.
4. Objects, Constructors, and Destructors
new
constructs objects; delete
calls the destructor and frees memory. For arrays of objects, use new[]
/delete[]
to ensure all destructors run.
#include <iostream>
struct Widget {
Widget() { std::cout << "Widget constructed\n"; }
~Widget() { std::cout << "Widget destroyed\n"; }
};
int main() {
Widget* w = new Widget; // single object
delete w;
Widget* wa = new Widget[2]; // array of objects
delete[] wa;
}
Output (example)
Widget constructed
Widget destroyed
Widget constructed
Widget constructed
Widget destroyed
Widget destroyed
5. Best Practices (RAII and Smart Pointers)
Prefer RAII: resource acquisition is initialization. In modern C++, use smart pointers (std::unique_ptr
, std::shared_ptr
) to manage ownership automatically. They call delete
for you.
5.1 unique_ptr for Single Objects
#include <memory>
#include <iostream>
int main() {
auto p = std::make_unique<int>(123);
std::cout << *p << "\n"; // auto-freed when out of scope
}
5.2 unique_ptr for Arrays
#include <memory>
#include <iostream>
int main() {
std::unique_ptr<int[]> arr(new int[3]{10,20,30});
for (int i = 0; i < 3; ++i) std::cout << arr[i] << " ";
std::cout << "\n"; // auto-freed
}
📝 Try it Yourself: Replace a raw
new
/delete
example withstd::unique_ptr
and confirm no manualdelete
is needed.
6. Common Pitfalls to Avoid
- Mismatched delete: Use
delete
fornew
anddelete[]
fornew[]
. Mixing them is undefined behavior. - Memory leaks: Forgetting
delete
/delete[]
leaks memory. Prefer RAII and smart pointers to prevent leaks. - Dangling pointers: Don’t use a pointer after deleting it. Set it to
nullptr
immediately. - Unnecessary dynamic allocation: Prefer automatic storage or containers like
std::vector
unless dynamic allocation is truly needed.
🔹 Frequently Asked Questions (FAQ)
Q: What happens if I use delete
instead of delete[]
?
A: It’s undefined behavior for arrays; not all destructors may run, and memory can be corrupted or leaked.
Q: Do I always need dynamic memory?
A: No. Prefer stack variables and standard containers (std::vector
, std::string
, etc.). Use dynamic allocation only when lifetimes or sizes must be decided at runtime.
Q: Is new
/delete
still used in modern C++?
A: Yes, but idiomatic modern C++ wraps ownership in RAII (smart pointers/containers), minimizing raw new
/delete
in application code.