Before You Blame C++ for Memory Safety Issues, Look in the Mirror
Dr. Dennis Akpenyi
- 6 minutes readThe Spark of the Conversation
On October 16th, 2025, I joined the online pre-conference interview for Meeting C++ 2025, featuring Klaus Iglberger, author of C++ Software Design, and hosted by Jens Weller, the chief organizer of Meeting C++.
During the session, Klaus voiced his frustration with the ongoing wave of C++ bashing and the misinformation that continues to surround the language. He argued that the so-called “safety problem” critics point to is less about the language itself and more about how it’s used—and I couldn’t agree more.
From my experience, the loudest opinions about C++ often come not from hands-on practitioners, but from secondhand interpretations floating around Reddit, Hacker News, X, or Discord debates rather than from real-world engineering.
Perhaps the real problem isn’t C++ itself, but how—and by whom—it’s being judged.
The White House Report
This discussion immediately brought to mind the February 2024 White House report, “Back To The Building Blocks: A Path Toward Secure And Measurable Software.”
The report rightly calls on the software industry to build a safer and more secure cyberspace, urging programming language designers, developers, and organizations alike to do better.
However, one statement in particular stood out:
“Experts have identified a few programming languages that both lack traits associated with memory safety and also have high proliferation across critical systems, such as C and C++.”
This phrasing traces back to Alex Gaynor’s 2019 article, “Introduction to Memory Unsafety for VPs of Engineering,” which sought to steer engineering leaders away from so-called “memory-unsafe” languages like C and C++, and toward “memory-safe” ones such as Python, Go, and Rust.
While the intent was noble, it also amplified the perception that C++ is inherently unsafe—an oversimplification that ignores how much the language has evolved.
Memory Safety Issues in C++
Yes, memory safety issues exist—and they can be a developer’s nightmare. Broadly speaking, they fall into two categories:
- Spatial safety issues: accessing memory outside an object’s bounds (e.g., out-of-bounds reads/writes, buffer overflows).
- Temporal safety issues: accessing memory after its lifetime has ended (e.g., use-after-free, double-free, dangling pointers).
But here’s the real question: Has modern C++ addressed these problems? Absolutely—and in meaningful, standard-backed ways.
Starting with C++11 and continuing through C++17, C++20, and C++23, the language has introduced mechanisms that directly target both spatial and temporal memory issues:
- RAII (Resource Acquisition Is Initialization) — ensures that resources (memory, files, sockets, etc.) are released automatically when their owners go out of scope.
- Smart pointers (
std::unique_ptr,std::shared_ptr,std::weak_ptr) — encode ownership and lifetimes explicitly, reducing memory leaks and double frees. - Move semantics — eliminate unnecessary copies and prevent double deletions.
- Value semantics and references — reduce or eliminate the need for raw pointers entirely.
- Static and runtime analysis tools (ASan, UBSan, clang-tidy, cppcheck) — detect potential violations early.
- C++ Core Guidelines — provide evolving, community-driven best practices for writing safer code.
These aren’t superficial improvements—they encode ownership, lifetime, and intent directly into the type system. The tools to write memory-safe C++ exist. It’s how we use the language that matters most.
Not the Language, but the Practice
So, is Alex Gaynor justified in suggesting that new projects avoid “memory-unsafe” languages altogether? That’s debatable.
Before organizations rush to rewrite or migrate entire codebases, they might consider expert-led refactoring instead. Some teams have even chosen to call into their existing C++ code from Rust adding complexity and maintenance overhead. While that may be justified in some contexts, it’s often a sign that the original C++ codebase wasn’t modernized properly.
Getting C++ right from the start—by embracing modern idioms, safety mechanisms, and coding discipline—eliminates the need for such band-aid solutions. C++ can be modernized incrementally, one component at a time.
Rust – A Valuable, but Different Approach
Speaking of Rust: earlier this year, I attended a Rust NYC meetup in New York City where Brendan O’Brien, CEO of n0-computer, presented the iroh project in his talk “Betting the Farm on Rust.”
He shared insights on building a Rust-based startup and the effort it took to get their MVP right. He emphasized that while Rust’s borrow checker enforces safety, it also demands patience, time, and a deep understanding to appease it.
Rust was born partly out of frustration with the memory pitfalls that plague systems languages like C and C++. The design choices behind its creation are both brilliant and deliberate — so much so that its inclusion in the Linux kernel (6.1) by Linus Torvalds in 2022 stands as a testament to its safety and maturity.
Yet while Rust offers safety by design, C++ enables safety by discipline. Both philosophies are valid; the difference isn’t moral but philosophical — Rust enforces correctness by default, whereas C++ entrusts it to the programmer.
And yes, Rust also provides an unsafe mode for those moments when bypassing the guardrails is necessary.
A Journey with C++
In my early C++ days, I was startled by the realities of manual memory management. I made my share of mistakes. But as I gained experience, my fascination and respect for the language deepened.
C++ feels like being handed the keys to an empire - vast, powerful, and full of possibilities. Yes, you can shoot yourself in the foot if you’re not careful, but that’s the price - and the privilege of power.
Every language imposes trade-offs. Some grant freedom at the cost of responsibility; others enforce safety through constraints. Neither is superior — they simply reflect different design philosophies.
Evolve with the Language
If you’ve chosen a language, evolve with it. Legacy C++ projects often suffer not from the language itself, but from technical debt left by teams who stopped learning when the language evolved. Don’t let that debt compound.
Practical steps to write safer, modern C++:
Leverage Your Toolchain: Use the latest compilers with strict warnings, and integrate static analyzers (Clang-Tidy) and runtime sanitizers (ASan, UBSan) into your CI/CD pipeline.
Refactor Incrementally for Safety: Prioritize replacing raw pointers with smart pointers and using standard library containers. Let the C++ Core Guidelines and its support tools direct your efforts.
Build a Robust Safety Net: Write unit tests for new code and maintain a fast CI pipeline that runs tests, analysis, and sanitizers on every change.
Document for Clarity and Contract: Focus documentation on API contracts, ownership semantics, and internal patterns. Keep it version-controlled and close to the code.
Invest in Continuous Learning: Use code reviews as a primary teaching tool. Encourage exploration of modern standards (C++17/20/23) through internal talks and study groups.
There’s a wealth of free knowledge from conferences like CppCon, CppNow, CppNorth, CoreCpp, Meeting C++, C++OnSea, and ACCU Conference, all available on YouTube, with sessions dedicated to memory safety, modern patterns, and real-world best practices.
I also recommend Scott Meyers’ Effective Modern C++ and Patrice Roy’s C++ Memory Management — two outstanding guides to writing modern, safe, expressive and idiomatic C++.
I’ve programmed professionally in Python, Go, C++, Elixir and Rust, and I admire the unique beauty of each. Every language reflects a philosophy:
- C++ trusts you with control.
- Rust guards you with constraints.
- Go frees you through simplicity.
- Python empowers you with clarity.
- Elixir/Erlang sustain you with resilience.
Whichever language you choose, commit to mastering it — and to evolving with it.
Before you bash a language, remember: behind every language and its standard libraries lies years of hard work, collaboration, and dedication from the people who build and maintain it. Respect that effort.
Learn the craft. Evolve with your tools. And go forth to write safe, beautiful, and enduring code.