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
nfrom input, allocate an array ofnints, 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 destroyed5. 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/deleteexample withstd::unique_ptrand confirm no manualdeleteis needed.
6. Common Pitfalls to Avoid
- Mismatched delete: Use
deletefornewanddelete[]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
nullptrimmediately. - Unnecessary dynamic allocation: Prefer automatic storage or containers like
std::vectorunless 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.