👆Click the blue text "Linux Armory" at the top, and select "Add to Favorites" in the upper right corner
to not miss out on great articles and see valuable content first.

👆FollowLinux Armory, to receive hardcore Linux learning materials and code.
Today, let’s revisit smart pointers in C++.
In C++ resource management, manually using <span>new</span>/<span>delete</span> can easily lead to memory leaks, dangling pointers, and exception safety issues. The smart pointers introduced in C++11 encapsulate ordinary pointers with RAII (Resource Acquisition Is Initialization): they automatically release resources when the object goes out of scope, making programs safer and easier to maintain.
Types of Smart Pointers
The standard library <span><memory></span> provides three core types of smart pointers:
| Type | Ownership Model | Characteristics | Typical Scenarios |
|---|---|---|---|
<span>std::unique_ptr</span> |
Exclusive | Uniquely owns the resource, movable but not copyable | Exclusive resource management (files, sockets, mutexes) |
<span>std::shared_ptr</span> |
Shared | Multiple pointers share ownership, reference counting | Shared caches, plugin systems |
<span>std::weak_ptr</span> |
Observer | Does not own the resource, only observes <span>shared_ptr</span> |
Breaking circular references, weak reference caches |
The earlier
<span>std::auto_ptr</span>has been deprecated in C++11 and is no longer used.
std::unique_ptr — Exclusive Ownership
<span>unique_ptr</span> represents a pointer that uniquely owns the resource. Characteristics:
- Copying is prohibited; it can only be moved (
<span>std::move</span>). - Automatically calls
<span>delete</span>or a custom deleter when the scope ends.
Basic Usage
#include <memory>
#include <iostream>
struct Foo
{
Foo() { std::cout << "Foo()\n"; }
~Foo() { std::cout << "~Foo()\n"; }
};
int main()
{
std::unique_ptr<Foo> p1 = std::make_unique<Foo>(); // Recommended way
/* p1 is null, no longer pointing to the object, p2 goes out of scope, automatically delete */
std::unique_ptr<Foo> p2 = std::move(p1); // Transfer ownership
}
Scenarios
- RAII Encapsulation: Commonly used for exclusive resources such as file descriptors, sockets, mutexes, etc.
- Factory Function Returns: Clearly indicates ownership transfer.
std::shared_ptr — Reference Counting Shared
<span>shared_ptr</span> allows multiple pointers to share ownership of the same object. It internally maintains a control block that contains:
- Reference count (thread-safe)
- Deleter
Basic Usage
#include <memory>
#include <iostream>
struct Foo
{
Foo() { std::cout << "Foo()\n"; }
~Foo() { std::cout << "~Foo()\n"; }
};
int main()
{
auto sp1 = std::make_shared<Foo>();
std::shared_ptr<Foo> sp2 = sp1; // Reference count +1
std::cout << sp1.use_count() << '\n'; // Output 2
// The last shared_ptr destructs and automatically deletes
}
Circular Reference Scenario
#include<iostream>
#include<memory>
class Test2;
class Test1
{
public:
std::shared_ptr<Test2> test2_ptr;
~Test1()
{
std::cout << "Test1 has been destroyed."<< std::endl;
}
};
class Test2
{
public:
std::shared_ptr<Test1> test1_ptr;
~Test2()
{
std::cout << "Test2 has been destroyed."<< std::endl;
}
};
int main()
{
std::shared_ptr<Test1> test1 = std::make_shared<Test1>();
std::shared_ptr<Test2> test2 = std::make_shared<Test2>();
test1->test2_ptr = test2; // Test1 references Test2
test2->test1_ptr = test1; // Test2 references Test1
// Due to circular references, the destructors of Test1 and Test2 will not be called, leading to memory leaks
return 0;
}
Typical Applications
- Multi-module Shared Resources: For example, cache objects, configuration centers.
- Observer Pattern: Event subscribers hold the same subject object.
Considerations
- Circular References: Objects referencing each other with
<span>shared_ptr</span>will never be released, requiring<span>weak_ptr</span>to resolve. - Overhead: Additional control block allocation and atomic operations.
std::weak_ptr — Weak Reference Observer
<span>weak_ptr</span>does not own the object, it only observes <span>shared_ptr</span>.
- Does not increase the reference count.
- Can use
<span>lock()</span>to safely obtain a<span>shared_ptr</span>, returning null if the object has been destroyed.
Example
#include <memory>
#include <iostream>
struct Foo
{
Foo()
{
std::cout<<"Foo()\n";
}
~Foo()
{
std::cout<<"~Foo()\n";
}
};
int main()
{
auto sp = std::make_shared<Foo>();
std::weak_ptr<Foo> wp = sp; // Does not increase reference count
if (auto p = wp.lock())
{ // Attempt to promote
std::cout << "Object still exists\n";
}
} // After sp destructs, wp.lock() returns null
Typical Scenarios
- Breaking Circular References: In bidirectionally associated objects, one side holds a
<span>shared_ptr</span>, while the other holds a<span>weak_ptr</span>. - Caching/Observer: Only need to check if the object is alive, without affecting its lifecycle.
Common Methods and Techniques
-
Prefer using
<span>std::make_unique</span>/<span>std::make_shared</span>
- Avoid raw
<span>new</span>, better exception safety. <span>make_shared</span>allocates the object and control block in one go, more efficient.
Custom Deleters are suitable for file handles, database connections:
std::shared_ptr<FILE> fp(fopen("a.txt","r"), fclose);
Performance Considerations If sharing is not needed, avoid the atomic counting overhead of <span>shared_ptr</span>.
Lifecycle Management Smart pointers are just tools for automatic release; still need to ensure they are not released too early or too late logically.
Application Example: Bidirectional Tree Node
struct Node
{
int value;
std::shared_ptr<Node> left;
std::weak_ptr<Node> parent; // Prevent circular references
};
<span>left</span> owns child nodes; <span>parent</span> only observes the parent node, avoiding circular references that lead to leaks.
Below, we use <span>std::shared_ptr</span> and <span>std::weak_ptr</span> to construct bidirectional (parent-child mutual reference) tree nodes, preventing memory leaks caused by circular references.
Example
#include <iostream>
#include <memory>
#include <string>
// Bidirectional tree node
struct Node
{
std::string name;
std::shared_ptr<Node> left; // Owns child nodes
std::shared_ptr<Node> right; // Owns child nodes
std::weak_ptr<Node> parent; // Only observes the parent node, avoiding circular references
explicit Node(std::string n) : name(std::move(n))
{
std::cout << "Construct: " << name << "\n";
}
~Node()
{
std::cout << "Destruct : " << name << "\n";
}
};
// Helper function: establish parent-child relationship
void setChild(const std::shared_ptr<Node>& parent,
const std::shared_ptr<Node>& child,
bool isLeft)
{
if (isLeft) parent->left = child;
else parent->right = child;
child->parent = parent; // weak_ptr points to parent node
}
int main()
{
// Create root node
auto root = std::make_shared<Node>("root");
// Create left and right child nodes and establish relationships
auto leftChild = std::make_shared<Node>("left");
auto rightChild = std::make_shared<Node>("right");
setChild(root, leftChild, true);
setChild(root, rightChild, false);
// Access parent node: need to lock first
if (auto p = leftChild->parent.lock())
{
std::cout << leftChild->name << "'s parent is " << p->name << "\n";
}
// After the scope ends, root/left/right will all be automatically destructed
// Because parent is weak_ptr, it will not form circular references
return 0;
}
Output
Construct: root
Construct: left
Construct: right
left's parent is root
Destruct : left
Destruct : right
Destruct : root
As we can see:
- All nodes are correctly destructed, with no memory leaks.
<span>parent</span>uses<span>weak_ptr</span>, even if<span>child</span>points to<span>parent</span>, it does not increase the reference count, thus avoiding circular references.
Key Points
-
The parent pointer must be
<span>weak_ptr</span>If the parent pointer is<span>shared_ptr</span>,<span>root</span>and<span>leftChild</span>will hold each other, and the reference count will never reach zero, leading to memory leaks. -
Use
<span>lock()</span>when accessing the parent node<span>weak_ptr::lock()</span>will return a temporary<span>shared_ptr</span>, allowing safe access when the object exists; if the object has been destroyed,<span>lock()</span>returns a null pointer. -
RAII Automatic Management No need for manual
<span>delete</span>, all resources are automatically released when going out of scope, ensuring exception safety.
In this way, we can easily implement bidirectional trees, graph structures, or any object model requiring bidirectional references, while avoiding the most common pitfalls of smart pointers—circular references.
Selection
| Requirement | Recommendation |
|---|---|
| Exclusive Resource | <span>unique_ptr</span> |
| Multiple Sharing | <span>shared_ptr</span> |
| Only Observing | <span>weak_ptr</span> |
Rule of Thumb: Use
<span>unique_ptr</span>whenever possible instead of<span>shared_ptr</span>.
Conclusion
The core of smart pointers is RAII + Ownership Model:
- Exclusive Ownership:
<span>unique_ptr</span> - Reference Counting Shared:
<span>shared_ptr</span> - Weak Observation:
<span>weak_ptr</span>
Choosing smart pointers wisely can greatly reduce the risk of memory leaks, simplify resource management, and write safe, concise, and modern C++ code.
Reading a hundred times, the meaning will naturally appear. Coding is the same, always fresh.
↓↓Recommended Articles↓↓
👉 Detailed method for creating library files in Linux
👉 Summary of the most commonly used commands in Linux (selected)
👉 Step-by-step guide to writing a Linux thread pool
👉 Summary of commonly used methods in Linux shell programming
👉 Essence of C++ basic knowledge
↓↓Recommended Collections↓↓
👉 Collection of C Language Introductory Tutorials
👉 Collection of Common Software Tools
👉 Collection of Linux Knowledge
👉 Collection of Detailed Linux Libraries
👉 Linux Application Programming Collection

❝
Follow Linux Armory, reply in the chat interface with 「1024」 to get all Linux materials and code.
❞
Technical skills require step-by-step accumulation, building up to great results.「Do not regret wasting years, do not be ashamed of being mediocre」
Improve a little every day, support the author, remember to like and share! Add the assistant’s WeChatto receive numerous classic open-source project source codes, read anytime and anywhere, there is always one that can help you!
Support the author, please extend your little hand to wealth
Share, collect👇👇👇👇👇Like, watch
Support quality content with action!