Real‑world examples of memory leaks in C++ and how to fix them
Starting with real examples of memory leaks in C++
Let’s start with concrete code. These are not contrived toy snippets; they mirror the kind of examples of memory leaks in C++ that show up in bug trackers for real products.
Example of a basic leak: forgetting delete
void loadConfig() {
char* buffer = new char[1024];
if (!readConfigFromDisk(buffer, 1024)) {
// Oops: we just return without deleting buffer
return;
}
// ... use buffer ...
delete[] buffer; // never reached on early return
}
This is the classic example of a memory leak in C++: allocated with new[], never released on some code paths. In a long‑running server, repeating this thousands of times means the process RSS just climbs and never comes down.
Fix with RAII and smart pointers:
#include <vector>
void loadConfig() {
std::vector<char> buffer(1024);
if (!readConfigFromDisk(buffer.data(), buffer.size())) {
return; // no leak, vector cleans up
}
// ... use buffer ...
}
This is one of the best examples of why resource ownership should be tied to object lifetime, not to manual new/delete pairs.
Examples of examples of memory leaks in C++ with exceptions
The most painful examples of memory leaks in C++ tend to involve exceptions. A throw that jumps over a delete is a silent leak.
Example of leak on exception path
#include <string>
std::string readUserName() {
char* buf = new char[256];
if (!readFromSocket(buf, 256)) {
delete[] buf;
throw std::runtime_error("read failed");
}
std::string name(buf); // might throw std::bad_alloc
delete[] buf;
return name;
}
If std::string’s constructor throws (low memory, for instance), the delete[] buf; is never executed. That’s a leak.
RAII fix using std::unique_ptr with custom deleter:
#include <memory>
#include <string>
std::string readUserName() {
std::unique_ptr<char[], void(*)(char*)> buf(
new char[256],
[](char* p) { delete[] p; }
);
if (!readFromSocket(buf.get(), 256)) {
throw std::runtime_error("read failed");
}
std::string name(buf.get()); // if this throws, buf still gets freed
return name;
}
Modern C++ gives you better examples of safe patterns than raw pointers ever could. If an exception skips the rest of the function, the smart pointer’s destructor still runs.
Container and collection examples include subtle leaks
Not all leaks are about a missed delete. Some examples of memory leaks in C++ come from misusing containers.
Example of leaking through std::vector of raw pointers
#include <vector>
struct Widget { /* ... */ };
void buildWidgets() {
std::vector<Widget*> widgets;
for (int i = 0; i < 1000; ++i) {
Widget* w = new Widget();
widgets.push_back(w);
}
widgets.clear(); // memory for Widgets is never freed
}
The vector’s own buffer gets freed, but the Widget objects are still sitting on the heap. This is a textbook example of a logical memory leak in C++.
Fix using std::unique_ptr:
#include <memory>
void buildWidgets() {
std::vector<std::unique_ptr<Widget>> widgets;
for (int i = 0; i < 1000; ++i) {
widgets.push_back(std::make_unique<Widget>());
}
widgets.clear(); // destroys Widgets automatically
}
Now the vector holds ownership, and clearing it releases memory deterministically.
Example of leak via erase without delete
#include <map>
struct Session { /* ... */ };
std::map<int, Session*> sessions;
void removeSession(int id) {
auto it = sessions.find(id);
if (it != sessions.end()) {
sessions.erase(it); // Session object is never deleted
}
}
This pattern shows up constantly in older server code. The map entry goes away, but the Session object leaks.
Better pattern: use std::unique_ptr<Session> in the map, or at least remember to delete it->second before erasing.
Real examples of memory leaks in C++ with singletons and globals
Global objects and singletons are another source of examples of memory leaks in C++ that are easy to miss in code review.
Example of a leaked singleton
class Logger {
public:
static Logger* instance() {
static Logger* inst = new Logger();
return inst; // never deleted
}
// ...
};
``
In many apps, this is effectively a permanent leak. The process exits and the OS reclaims memory, but it still counts as a leak from a correctness standpoint, and tools like Valgrind will happily report it.
**Modern C++ fix using function‑local static:**
```cpp
class Logger {
public:
static Logger& instance() {
static Logger inst; // destroyed at program shutdown
return inst;
}
};
This avoids both the leak and potential initialization order issues.
Example of static container that only grows
#include <string>
#include <vector>
std::vector<std::string>* getCache() {
static std::vector<std::string>* cache = new std::vector<std::string>();
return cache; // never freed, and may grow unbounded
}
Even if you never new again after startup, data kept in a global cache that only grows is a practical memory leak. In 2024, this pattern is still one of the best examples of how “just cache it” can turn into “why is the service using 4 GB?”
Callback and event system examples of memory leaks in C++
Event systems and callbacks often hide ownership bugs. These examples of examples of memory leaks in C++ are particularly nasty because the leak is tied to application logic.
Example of leaked listener
#include <vector>
struct Listener {
virtual void onEvent(int) = 0;
virtual ~Listener() = default;
};
std::vector<Listener*> listeners;
void registerListener(Listener* l) {
listeners.push_back(l); // ownership? unclear
}
void shutdown() {
listeners.clear(); // listeners never deleted
}
Here, nobody knows who owns the listener. That ambiguity is usually the first sign of a leak.
Improved approach using smart pointers:
#include <memory>
#include <vector>
std::vector<std::unique_ptr<Listener>> listeners;
void registerListener(std::unique_ptr<Listener> l) {
listeners.push_back(std::move(l));
}
void shutdown() {
listeners.clear(); // all listeners destroyed
}
Ownership is explicit, and the leak disappears.
Example of async callback capturing raw pointer
#include <thread>
struct Job {
void run();
};
void startJobAsync() {
Job* job = new Job();
std::thread([job]() {
job->run();
// forgot: delete job;
}).detach();
}
The detached thread finishes, but the Job object stays allocated forever. This is a real example of a memory leak in C++ that shows up regularly in modern, multi‑threaded code.
Fix using std::unique_ptr and move capture:
void startJobAsync() {
auto job = std::make_unique<Job>();
std::thread([job = std::move(job)]() mutable {
job->run();
// unique_ptr destructor runs when lambda ends
}).detach();
}
No raw pointers, no leak.
Modern C++ (C++17/20) patterns that avoid leaks by design
The best examples of leak‑free C++ code in 2024 barely use new at all. Instead, they lean on language features and the standard library.
Example of RAII for file and buffer together
#include <fstream>
#include <string>
std::string readFile(const std::string& path) {
std::ifstream file(path, std::ios::binary);
if (!file) throw std::runtime_error("open failed");
std::string data(
(std::istreambuf_iterator<char>(file)),
std::istreambuf_iterator<char>()
);
return data; // no explicit heap management
}
No raw new, no manual delete, no leak.
Example of using std::shared_ptr correctly
std::shared_ptr can both prevent and cause leaks. Here’s a good example:
#include <memory>
struct Image { /* heavy data */ };
std::shared_ptr<Image> loadImage(const std::string& path) {
static std::weak_ptr<Image> cache;
if (auto cached = cache.lock()) {
return cached;
}
auto img = std::make_shared<Image>();
// ... load data ...
cache = img;
return img;
}
Ownership is shared but bounded. When all users drop their shared_ptr, the image is freed and the weak pointer stops the cache from keeping it alive.
Example of shared_ptr cycle causing a leak
For contrast, here’s an example of examples of memory leaks in C++ using std::shared_ptr incorrectly:
#include <memory>
struct Node {
std::shared_ptr<Node> next;
std::shared_ptr<Node> prev; // cycle
};
void buildCycle() {
auto a = std::make_shared<Node>();
auto b = std::make_shared<Node>();
a->next = b;
b->prev = a; // reference cycle, use_count never hits zero
}
Even after buildCycle returns, the two nodes keep each other alive. That’s a logical leak. The fix is to use std::weak_ptr for back‑references.
Tooling: finding real examples of memory leaks in C++ in 2024
If you want more than hypothetical examples of memory leaks in C++, you need tools that can catch them in your own code.
Popular options in 2024 include:
- AddressSanitizer (ASan): Built into Clang and GCC, catches leaks, use‑after‑free, and buffer overflows. See the LLVM docs at: https://llvm.org/docs/AddressSanitizer.html
- Valgrind / Memcheck: Still widely used on Linux for native C++ apps. Official docs: https://valgrind.org/docs/manual/mc-manual.html
- Visual Studio Diagnostics Tools: For Windows C++ development, with leak detection integrated into the IDE.
While these are not .gov or .edu sites, they are the authoritative sources for these tools. For broader background on software reliability and testing practices, university CS departments such as MIT (https://ocw.mit.edu) and Stanford (https://cs.stanford.edu) publish course material that often includes debugging and memory analysis techniques.
Running your own program under ASan or Valgrind will give you real examples of memory leaks in C++ drawn from your codebase, complete with stack traces pointing at the offending new.
FAQ: short answers with examples
What is a simple example of a memory leak in C++?
A simple example is allocating with new and never calling delete:
void foo() {
int* p = new int(42);
// forgot: delete p;
}
Every call to foo leaks one int.
Do smart pointers completely prevent memory leaks?
No. Smart pointers remove many classes of leaks, but examples include:
- Reference cycles with
std::shared_ptr std::unique_ptr::release()without a laterdelete- Storing raw pointers obtained from smart pointers and misusing them
They’re safer, but not magic.
Are memory leaks always a problem if the OS cleans up on exit?
Not always, but they’re still a code smell. For short‑lived command‑line tools, a tiny leak might not matter. For servers, games, and embedded systems, leaks accumulate over hours or days and become user‑visible failures. Real examples of memory leaks in C++ from production systems often look minor in isolation but add up under load.
How can I get more real examples of memory leaks in C++ to study?
Run your own test suite under AddressSanitizer or Valgrind and look at the reports. Those tools will hand you examples of examples of memory leaks in C++ that are specific to your project, complete with line numbers and call stacks. That’s far more educational than reading only textbook snippets.
What are some best examples of patterns that avoid leaks?
Some of the best examples include:
- Using RAII for every resource: memory, files, sockets, mutexes
- Prefer
std::vector,std::string, and other containers over raw arrays - Using
std::unique_ptrfor ownership andstd::weak_ptrto break cycles - Avoiding
newin application code; confine it to factory functions if needed
If you adopt those patterns consistently, most examples of memory leaks in C++ simply stop appearing in your code reviews.
Related Topics
Explore More Memory Leaks
Discover more examples and insights in this category.
View All Memory Leaks