In brokerage HTS/MTS server and client systems that must process a massive stream of orders and market data around the clock, a memory leak is not just a simple bug — it's a critical flaw that can bring the whole service down.
Back in the C++03 days, it was common for memory to leak because an exception was thrown after a new allocation, or a delete call simply got lost somewhere inside a tangle of conditional branches.
But in modern C++ (C++11 and later), smart pointers built on the RAII (Resource Acquisition Is Initialization) principle let us guarantee "zero memory leaks" as a matter of design. In this post, I'll go over how to use each smart pointer and what to watch out for, from the perspective of real-world HTS/MTS development.
1. The Foundation of Smart Pointers: The RAII Principle
It's a pattern that ties ownership of a resource (heap memory, a socket, a file handle, etc.) to a stack-based object. When that stack object goes out of its scope and is destroyed, its destructor is automatically called, safely releasing the underlying heap memory.
2. std::unique_ptr: Exclusive Ownership
The first smart pointer you should reach for in real-world work is std::unique_ptr. As the name implies, **only a single object can hold ownership** of a given piece of heap memory.
std::unique_ptr has the same memory footprint (8 bytes on a 64-bit system) and performance as a raw pointer. Since it carries no overhead at all, you should default to it as your go-to smart pointer.
Basic Usage and Move Semantics
// [Example: using unique_ptr]
struct OrderPacket {
long long orderId;
int price;
};
void ProcessOrder() {
// 1. Safe construction with std::make_unique (C++14)
auto pOrder = std::make_unique<OrderPacket>();
pOrder->orderId = 100234;
pOrder->price = 75000;
// 2. Copying is forbidden (compile error)
// std::unique_ptr<OrderPacket> pCopy = pOrder;
// 3. Ownership transfer via std::move
std::vector<std::unique_ptr<OrderPacket>> orderQueue;
orderQueue.push_back(std::move(pOrder)); // pOrder is now nullptr
} // When the function scope ends, every object inside orderQueue is automatically deleted!
3. std::shared_ptr & std::weak_ptr: Shared Ownership and Avoiding Circular References
When several modules (for example, an execution manager, a screen UI renderer, and a networking module) need to reference the same resource at the same time, use the reference-counting-based std::shared_ptr.
Why You Should Use std::make_shared
// ❌ Bad example: this triggers two separate allocations — one for the object and one for the control block
std::shared_ptr<Session> pSession(new Session());
// ⭕ Good example: allocates the object memory and control block together in a single allocation (better speed and cache efficiency)
auto pSession = std::make_shared<Session>();
The Circular Reference Problem and std::weak_ptr
If two objects hold std::shared_ptr references to each other, their reference counts never drop to zero, so **a memory leak results**. To solve this, we use std::weak_ptr, a weak reference that doesn't affect the reference count.
The parent ➔ child direction should hold ownership via
std::shared_ptr,while the child ➔ parent direction should only observe, via
std::weak_ptr.
// [Safe way to access through a weak_ptr]
std::weak_ptr<Session> wSession = g_pSharedSession;
// weak_ptr can't be dereferenced directly -> call lock() to obtain a temporary shared_ptr
if (auto pSession = wSession.lock()) {
// Safe to access the memory as long as pSession stays valid
pSession->SendHeartbeat();
} else {
// The target memory has already been destroyed
}
4. Smart Pointer Guidelines for HTS/MTS Performance
- Default to
std::unique_ptr: Since it has no overhead, prefer exclusive ownership first unless there's a specific reason not to. - When passing function arguments: Pass
std::unique_ptrby value (moving it) only when you're actually transferring ownership; when you just need to reference the object, pass a raw pointer or a C++ reference instead. (Unnecessaryshared_ptrcopies incur the overhead of atomic increment/decrement operations on the reference count.) - Never call the raw constructor: Always use
std::make_uniqueandstd::make_sharedinstead ofnew T().
5. Wrapping Up
Smart pointers cleanly shut the door on memory leaks and dangling-pointer risks that arise when a C++ developer forgets a manual delete.
If you clearly separate who owns what, and make it a habit to use `unique_ptr`, `shared_ptr`, and `weak_ptr` in the right place for the right job, you can build a flawless HTS/MTS system.