From the public account: Program Meow Master
Memory management has always been a core and complex topic in C++. While raw pointers are straightforward to use, they are prone to issues such as memory leaks and double deletions. The emergence of smart pointers is a powerful tool in modern C++ to enhance code safety and maintainability.
Here, we will handwrite a simple smart pointer, helping you truly understand how smart pointers work.
The Dilemma of Raw Pointers
First, let’s look at a typical raw pointer code:
void foo() {
MyClass* p = new MyClass();
// Business logic
delete p; // Memory leak if forgotten
}
If <span>delete p</span> is forgotten, the program will leak memory; if it is written twice, the program may crash.
Manual management of raw pointers is not only cumbersome but also prone to subtle errors.
Design Goals of Smart Pointers
Smart pointers are essentially a class that encapsulates raw pointers and are responsible for:
- Automatically releasing resources to prevent memory leaks
- Clearly defining resource ownership to prevent double deletions
- Simplifying operations while supporting access similar to raw pointers
Handwriting a Unique Smart Pointer — SimpleSmartPtr
We will first implement a unique smart pointer that exclusively owns the pointer resource, disallowing copying and only allowing moving.
template<typename T>
class SimpleSmartPtr {
T* ptr;
public:
// Constructor, default nullptr
explicit SimpleSmartPtr(T* p = nullptr) : ptr(p) {}
// Destructor, releases resources
~SimpleSmartPtr() {
delete ptr;
}
// Disable copy constructor and copy assignment to prevent double deletion
SimpleSmartPtr(const SimpleSmartPtr<>) = delete;
SimpleSmartPtr& operator=(const SimpleSmartPtr<>) = delete;
// Support move construction, transferring resource ownership
SimpleSmartPtr(SimpleSmartPtr<&> other) noexcept : ptr(other.ptr) {
other.ptr = nullptr;
}
// Support move assignment, transferring resource ownership
SimpleSmartPtr& operator=(SimpleSmartPtr<&> other) noexcept {
if (this != &other) {
delete ptr; // Release old resource
ptr = other.ptr; // Take over new resource
other.ptr = nullptr; // Nullify old pointer
}
return *this;
}
// Dereference operator, supports pointer operations
T& operator*() const { return *ptr; }
T* operator->() const { return ptr; }
// Get raw pointer
T* get() const { return ptr; }
};
Key Points Explained:
- Constructor accepts a raw pointer, defaulting to
<span>nullptr</span>. - Destructor ensures that resources are automatically released when the pointer is destructed.
- Copy prohibition: Prevents multiple smart pointers from managing the same resource, avoiding double deletions.
- Move support: Allows the transfer of resource ownership between smart pointers, preventing resource waste.
- Operator overloading: Makes the smart pointer behave like a regular pointer for ease of use.
Why Prohibit Copying? Why Support Moving?
If copying is allowed:
SimpleSmartPtr<MyClass> p1(new MyClass());
SimpleSmartPtr<MyClass> p2 = p1; // Copy, both smart pointers point to the same raw pointer
Both smart pointers will attempt to <span>delete</span> the same pointer during destruction, leading to double deletion and program crashes.
Move semantics allow for safe transfer of resource ownership between smart pointers:
SimpleSmartPtr<MyClass> p2 = std::move(p1); // p2 takes over the resource, p1 is nullified
This avoids double deletion and allows for flexible resource transfer.
The Role of weak_ptr
When two objects hold each other via <span>shared_ptr</span><span>, it creates a </span><strong><span>circular reference</span></strong><span>, causing the count to never reach zero, leading to resource leaks.</span>
<span>weak_ptr</span><span> is a type of smart pointer that does not increase the reference count, specifically used to observe </span><code><span>shared_ptr</span><span> and avoid circular references.</span>
Conclusion
By handwriting smart pointers, you can not only:
- Understand the RAII (Resource Acquisition Is Initialization) concept
- Master the importance of move semantics and copy prohibition in design
- Comprehend resource ownership and lifecycle management
- Understand the basic principles of reference-counted smart pointers
It will also enable you to write safer, more efficient, and maintainable modern C++ code.
—END—