Best examples of dynamic memory allocation in C++: new and delete
Let’s skip the textbook definitions and start with concrete code. These examples of dynamic memory allocation in C++: new and delete are the patterns you actually see in real projects, from legacy servers to embedded firmware.
Example 1: Allocating a single object with new and cleaning up with delete
A classic example of dynamic memory allocation in C++: new and delete is a single object that outlives the current scope.
#include <iostream>
struct User {
int id;
std::string name;
};
int main() {
// Allocate a single User on the heap
User* u = new User{42, "Ada"};
std::cout << "User: " << u->id << ", " << u->name << '\n';
// Release the memory
delete u;
u = nullptr; // avoid dangling pointer
}
You still see this pattern in older codebases where a factory function returns a raw pointer. In 2024–2025, you’d usually wrap this in std::unique_ptr<User> instead, but understanding this basic example of new and delete is mandatory if you maintain legacy C++.
Example 2: Dynamic arrays with new[] and delete[]
Another frequent example of dynamic memory allocation in C++: new and delete is a raw dynamic array. This is common in performance-sensitive or embedded code where STL containers may be restricted.
#include <iostream>
int main() {
std::size_t n = 5;
// Allocate an array of n ints on the heap
int* data = new int[n];
for (std::size_t i = 0; i < n; ++i) {
data[i] = static_cast<int>(i * i); // 0, 1, 4, 9, 16
}
for (std::size_t i = 0; i < n; ++i) {
std::cout << data[i] << ' ';
}
std::cout << '\n';
// Must match new[] with delete[]
delete[] data;
data = nullptr;
}
Mixing new with delete[] or new[] with delete is undefined behavior. That’s one reason modern C++ code prefers std::vector<int> over raw arrays. But if you’re reading older code, this is one of the best examples to keep in mind.
Example 3: 2D dynamic array (matrix) using new and delete[]
Multidimensional data structures provide richer examples of dynamic memory allocation in C++: new and delete. Here’s a simple dynamic matrix:
#include <iostream>
int main() {
std::size_t rows = 3;
std::size_t cols = 4;
// Allocate an array of row pointers
int** matrix = new int*[rows];
// Allocate each row
for (std::size_t r = 0; r < rows; ++r) {
matrix[r] = new int[cols];
}
// Initialize and print
for (std::size_t r = 0; r < rows; ++r) {
for (std::size_t c = 0; c < cols; ++c) {
matrix[r][c] = static_cast<int>(r * cols + c);
std::cout << matrix[r][c] << ' ';
}
std::cout << '\n';
}
// Free in reverse order of allocation
for (std::size_t r = 0; r < rows; ++r) {
delete[] matrix[r];
}
delete[] matrix;
}
This pattern shows up in numerical code and some older scientific libraries. In modern C++, you’d probably use a single std::vector<int> of size rows * cols or a library like Eigen or Blaze, but this is still a very realistic example of how dynamic memory was traditionally handled.
Example 4: Custom RAII wrapper for new / delete
Before smart pointers were standardized, many teams wrote their own RAII wrappers. This is a more advanced example of dynamic memory allocation in C++: new and delete, where you wrap raw pointers in a small class that manages lifetime.
#include <utility>
template <typename T>
class ScopedPtr {
public:
explicit ScopedPtr(T* ptr = nullptr) noexcept : ptr_(ptr) {}
~ScopedPtr() {
delete ptr_;
}
ScopedPtr(const ScopedPtr&) = delete;
ScopedPtr& operator=(const ScopedPtr&) = delete;
ScopedPtr(ScopedPtr&& other) noexcept : ptr_(other.ptr_) {
other.ptr_ = nullptr;
}
ScopedPtr& operator=(ScopedPtr&& other) noexcept {
if (this != &other) {
delete ptr_;
ptr_ = other.ptr_;
other.ptr_ = nullptr;
}
return *this;
}
T* get() const noexcept { return ptr_; }
T& operator*() const { return *ptr_; }
T* operator->() const noexcept { return ptr_; }
private:
T* ptr_;
};
struct Connection {
// ...
};
int main() {
ScopedPtr<Connection> conn(new Connection{});
// use conn like a pointer: conn->...
}
This is conceptually similar to std::unique_ptr<T>. Understanding this pattern helps you refactor legacy RAII wrappers into standard smart pointers during modernization.
Example 5: Factory function returning a raw pointer
Many older APIs return raw pointers from factory functions. That’s another common example of dynamic memory allocation in C++: new and delete.
class Shape {
public:
virtual ~Shape() = default;
virtual double area() const = 0;
};
class Circle : public Shape {
public:
explicit Circle(double r) : r_(r) {}
double area() const override { return 3.141592653589793 * r_ * r_; }
private:
double r_;
};
Shape* make_circle(double radius) {
return new Circle(radius); // caller owns the memory
}
int main() {
Shape* s = make_circle(2.0);
double a = s->area();
// Later, caller must remember to free it
delete s;
}
In fresh 2024–2025 code, you’d usually return std::unique_ptr<Shape> instead, but you’ll still see this pattern in many commercial codebases and interview questions.
Example 6: Dynamic memory in a small object pool
High-performance systems sometimes manage memory manually to avoid frequent allocations. Here’s a trimmed-down example of dynamic memory allocation in C++: new and delete used to build a tiny object pool.
#include <vector>
#include <stack>
struct Particle {
float x, y, z;
};
class ParticlePool {
public:
explicit ParticlePool(std::size_t capacity) {
particles_.reserve(capacity);
for (std::size_t i = 0; i < capacity; ++i) {
particles_.push_back(new Particle{0.f, 0.f, 0.f});
freeList_.push(particles_.back());
}
}
~ParticlePool() {
while (!particles_.empty()) {
delete particles_.back();
particles_.pop_back();
}
}
Particle* acquire() {
if (freeList_.empty()) return nullptr; // pool exhausted
Particle* p = freeList_.top();
freeList_.pop();
return p;
}
void release(Particle* p) {
freeList_.push(p);
}
private:
std::vector<Particle*> particles_;
std::stack<Particle*> freeList_;
};
Production-grade pools are more sophisticated, but this shows a realistic way teams use multiple new calls up front, then recycle objects instead of allocating and freeing them constantly.
Example 7: Interop with C APIs that expect raw pointers
Even in 2025, plenty of C libraries require raw pointers and manual memory handling. Here’s an example of dynamic memory allocation in C++: new and delete in the context of C-style APIs.
extern "C" void c_api_process_buffer(char* buffer, int length);
void process_with_c_api(int length) {
char* buf = new char[length];
// initialize buffer
for (int i = 0; i < length; ++i) {
buf[i] = static_cast<char>('A' + (i % 26));
}
c_api_process_buffer(buf, length);
delete[] buf; // must be delete[], not delete
}
When wrapping a C library in a C++ interface, this is one of the best examples of how new / delete[] still show up in serious, modern code.
Modern trends (2024–2025): where new and delete still matter
If you’re following current C++ guidelines from the C++ Core Guidelines project or educational material from universities like MIT or Stanford, you’ll notice a clear trend: avoid naked new and delete in fresh code whenever possible.
Instead, modern C++ favors:
std::unique_ptrandstd::shared_ptrfor object lifetimesstd::vector,std::string, and other containers for dynamic storage- Custom allocators and memory resource (
std::pmr) for advanced performance tuning
So why bother with examples of dynamic memory allocation in C++: new and delete at all?
Because you still need to:
- Read and maintain large legacy systems where raw pointers are everywhere
- Pass data to and from C APIs that have no idea what a smart pointer is
- Understand the underlying model when debugging memory leaks, double frees, or use-after-free bugs
Tools like AddressSanitizer (ASan) and LeakSanitizer (LSan), widely used in 2024–2025 in CI pipelines, make it much easier to catch misuse of new and delete. But those tools work best when you already understand what correct usage looks like.
Authoritative sources such as the C++ Core Guidelines (maintained with input from ISO C++ committee members) strongly encourage RAII and resource ownership semantics that build on top of new and delete, not random manual calls scattered across the codebase.
Safer patterns that still rely on dynamic allocation under the hood
To round out these examples of dynamic memory allocation in C++: new and delete, it helps to see how modern idioms hide raw pointers while still using the heap.
Smart pointer example using std::unique_ptr
#include <memory>
struct Session {
// ...
};
std::unique_ptr<Session> make_session() {
// new is still called, but wrapped in a factory
return std::make_unique<Session>();
}
int main() {
auto session = make_session();
// No explicit delete needed; RAII handles cleanup
}
Internally, std::make_unique uses new to allocate the Session. The difference is that ownership is explicit, and delete is guaranteed when the unique_ptr goes out of scope.
Container example using std::vector
#include <vector>
int main() {
std::vector<int> values;
values.reserve(1000); // may cause a heap allocation
for (int i = 0; i < 1000; ++i) {
values.push_back(i);
}
}
std::vector allocates memory dynamically, but you never see new or delete directly. Understanding the earlier raw-pointer examples makes it easier to reason about what vector is doing behind the scenes, especially when you debug performance or memory usage.
Common mistakes when using new and delete
Looking at real examples of dynamic memory allocation in C++: new and delete also means talking about how they go wrong. A few classic pitfalls:
- Forgetting to call
deleteordelete[]→ memory leak - Calling
deletetwice on the same pointer → double free - Using
deletewheredelete[]is required (or vice versa) → undefined behavior - Using a pointer after it’s been deleted → use-after-free
- Throwing exceptions between
newanddeletewithout RAII → leaked memory
Modern coding standards strongly recommend:
- Immediately wrapping raw
newallocations in a smart pointer - Initializing pointers to
nullptrand resetting them afterdelete - Using RAII objects so cleanup happens automatically
These habits keep your examples of dynamic memory allocation in C++: new and delete from turning into late-night debugging sessions.
FAQ: common questions about new, delete, and real examples
What is a simple example of dynamic memory allocation with new in C++?
A simple example of dynamic memory allocation in C++ is:
int* p = new int(10);
// use *p
delete p;
This allocates an int on the heap, initializes it to 10, and then frees it.
What are typical real examples of using new[] and delete[]?
Typical real examples include dynamically sized arrays when the size isn’t known at compile time, such as buffers for I/O, image data, or numerical grids:
std::size_t n = get_runtime_size();
char* buffer = new char[n];
// ... use buffer ...
delete[] buffer;
Should I still use new and delete in new C++ code?
In most new code, you should avoid naked new and delete. Prefer std::unique_ptr, std::shared_ptr, and standard containers. However, you must understand these examples of dynamic memory allocation in C++: new and delete to work effectively with legacy systems, embedded environments, or low-level libraries.
How do I debug memory leaks from new / delete?
Use tools like AddressSanitizer and LeakSanitizer, or platform-specific tools such as Visual Studio’s built-in diagnostics. These tools track allocations and deallocations and report leaks or invalid frees. The patterns you saw in the code examples above map directly to what these tools report.
What’s the difference between new and malloc?
new constructs C++ objects and calls constructors, while malloc just reserves raw memory and returns void*. With malloc, you must cast and manually run constructors if needed. In idiomatic C++, new / delete (or better, RAII wrappers) are preferred.
The bottom line: you don’t have to love raw pointers, but you do need to recognize and reason about examples of dynamic memory allocation in C++: new and delete. That’s how you safely maintain older systems, wrap C libraries, and understand what your higher-level abstractions are doing under the hood.
Related Topics
Practical examples of polymorphism in C++: function overloading
Best examples of dynamic memory allocation in C++: new and delete
Practical examples of C# variable declaration and initialization examples
Modern examples of C++ operator overloading: custom operators examples that actually matter
Best examples of C# conditional statements: examples & explanations
Real‑world examples of C# exception handling: 3 practical patterns every developer should know
Explore More C++ Code Snippets
Discover more examples and insights in this category.
View All C++ Code Snippets