C++ 智能指针是 C++11 标准引入的自动内存管理工具,用于解决手动管理动态内存时的内存泄漏、野指针、重复释放等问题。其核心原理是利用 RAII(资源获取即初始化) 机制,将动态内存的生命周期绑定到智能指针的生命周期,当智能指针析构时自动释放所管理的内存
1.uinque_ptr
cpp
#include <memory>
#include <iostream>
using namespace std;
/*
特性:独占式所有权,同一时间只能有一个unique_ptr指向某块内存,禁止拷贝(但支持移动)。
适用场景:管理独占资源(如单个对象、数组),避免资源被共享。
*/
class uinque_ptr
{
private:
/* data */
public:
uinque_ptr(/* args */)
{
cout << "uinque_ptr constructed\n";
}
~uinque_ptr()
{
cout << "uinque_ptr destructed\n";
}
void display()
{
cout << "Inside display function of uinque_ptr\n";
}
};
int main()
{
// 创建一个 unique_ptr,管理一个 uinque_ptr 对象
std::unique_ptr<uinque_ptr> ptr1(new uinque_ptr());
// 使用 unique_ptr 访问 uinque_ptr 的成员函数
ptr1->display();
// 错误:不能复制 unique_ptr
// std::unique_ptr<uinque_ptr> ptr2 = ptr1; ERROR
// 转移所有权到另一个 unique_ptr
std::unique_ptr<uinque_ptr> ptr2 = std::move(ptr1);
if (!ptr1)
{
std::cout << "ptr1 is now null after move.\n";
}
// 使用新的 unique_ptr 访问 uinque_ptr 的成员函数
ptr2->display();
// 当 ptr2 超出作用域时,uinque_ptr 对象会被自动销毁
return 0;
}
核心原理:
- 核心原理
独占所有权:通过禁用拷贝构造和拷贝赋值,仅允许移动语义,确保同一时间只有一个unique_ptr指向某块内存。
RAII 绑定生命周期:unique_ptr对象存储一个指向动态内存的裸指针(T*)和一个删除器(Deleter,默认是std::default_delete)。当unique_ptr析构时,自动调用删除器释放所管理的内存。
cpp
template <typename T, typename Deleter = std::default_delete<T>>
class unique_ptr {
public:
// 禁用拷贝构造
unique_ptr(const unique_ptr&) = delete;
// 禁用拷贝赋值
unique_ptr& operator=(const unique_ptr&) = delete;
// 允许移动构造
unique_ptr(unique_ptr&& other) noexcept {
ptr_ = other.ptr_;
other.ptr_ = nullptr; // 转移所有权后,原指针置空
}
// 允许移动赋值
unique_ptr& operator=(unique_ptr&& other) noexcept {
if (this != &other) {
deleter_(ptr_); // 释放当前资源
ptr_ = other.ptr_;
other.ptr_ = nullptr;
}
return *this;
}
// 析构时释放资源
~unique_ptr() {
deleter_(ptr_);
}
private:
T* ptr_;
Deleter deleter_;
};
2.shared_ptr
cpp
#include <memory>
#include <iostream>
using namespace std;
/*
特性:共享式所有权,多个shared_ptr可指向同一内存,通过引用计数管理生命周期:
当新的shared_ptr指向对象时,引用计数 + 1;
当shared_ptr析构或重置时,引用计数 - 1;
引用计数为 0 时,自动释放内存。
适用场景:需要共享资源的场景(如多个对象共享同一数据)。
注意:
优先使用std::make_shared创建(更高效,避免内存泄漏);
避免循环引用(否则引用计数无法归零,导致内存泄漏,需配合std::weak_ptr解决)。
*/
class shared_Ptr
{
private:
/* data */
public:
shared_Ptr(/* args */)
{
cout << "shared_ptr constructed\n";
}
~shared_Ptr()
{
cout << "shared_ptr destructed\n";
}
void display()
{
cout << "Inside display function of shared_ptr\n";
}
};
int main()
{
// 创建一个 shared_ptr,管理一个 shared_ptr 对象
std::shared_ptr<shared_Ptr> ptr1 = std::make_shared<shared_Ptr>();
// 使用 shared_ptr 访问 shared_ptr 的成员函数
ptr1->display();
cout<<"Ref count:"<<ptr1.use_count()<<endl;
{
// 复制 shared_ptr,增加引用计数
std::shared_ptr<shared_Ptr> ptr2 = ptr1;
cout<<"Ref count after copy:"<<ptr1.use_count()<<endl;
// 使用新的 shared_ptr 访问 shared_ptr 的成员函数
ptr2->display();
}
cout<<"Ref count after ptr2 goes out of scope:"<<ptr1.use_count()<<endl;
// 当最后一个 shared_ptr 超出作用域时,shared_Ptr 对象会被自动销毁
ptr1.reset();
cout<<"Ref count after reset ptr1:"<<ptr1.use_count()<<endl;
return 0;
}
1. 核心原理
共享所有权:
通过引用计数(Reference Count) 跟踪指向同一资源的shared_ptr数量,当最后一个shared_ptr析构时,引用计数归零,释放资源。
双指针结构:
shared_ptr内部包含两个指针:
1.资源指针:指向实际管理的动态对象(T*);
2.控制块指针:指向一个独立的控制块(Control Block),控制块存储:
①引用计数(use_count):当前指向资源的shared_ptr数量;
②弱引用计数(weak_count):指向资源的weak_ptr数量;
③删除器(Deleter)和分配器(Allocator);
④资源的析构逻辑。
2. 底层实现关键
控制块的创建:
当通过std::make_shared()创建shared_ptr时,控制块与资源内存一次性分配(更高效);
当通过new T构造shared_ptr时,控制块单独分配。
c
template <typename T>
class shared_ptr {
public:
// 拷贝构造:引用计数+1
shared_ptr(const shared_ptr& other) {
if (other.control_block_) {
control_block_ = other.control_block_;
control_block_->use_count++; // 共享控制块,引用计数+1
}
}
// 析构:引用计数-1,归零则释放资源
~shared_ptr() {
if (control_block_) {
control_block_->use_count--;
if (control_block_->use_count == 0) {
delete control_block_->ptr; // 释放资源
if (control_block_->weak_count == 0) {
delete control_block_; // 弱引用也归零,释放控制块
}
}
}
}
private:
T* ptr_;
ControlBlock* control_block_; // 指向控制块
};
// 控制块结构(简化版)
struct ControlBlock {
T* ptr;
size_t use_count; // shared_ptr的引用计数
size_t weak_count; // weak_ptr的引用计数
Deleter deleter;
};
3.weak_ptr
cpp
#include <memory>
#include <iostream>
using namespace std;
/*
特性:弱引用,不拥有资源所有权,仅观察shared_ptr管理的资源,不影响引用计数。
适用场景:解决shared_ptr的循环引用问题(如双向链表节点、父子对象引用)。
*/
struct Node {
std::weak_ptr<Node> next; // 弱引用,避免循环引用
~Node() { std::cout << "Node destructed\n"; }
};
int main()
{
std::shared_ptr<Node> node1 = std::make_shared<Node>();
cout<<"Ref count of node1 1: "<<node1.use_count()<<endl;
std::shared_ptr<Node> node2 = std::make_shared<Node>();
cout<<"Ref count of node2 2: "<<node2.use_count()<<endl;
node1->next = node2; // 弱引用,node2的引用计数不变
node2->next = node1; // 弱引用,node1的引用计数不变
cout<<"Ref count of node1 after linking: "<<node1.use_count()<<endl;
return 0; // 引用计数为0,正常释放内存
}
1. 核心原理
弱引用
:不拥有资源所有权,仅观察shared_ptr管理的资源,不影响引用计数(use_count),但会关联到shared_ptr的控制块(增加weak_count)。
安全访问
:需通过lock()方法将weak_ptr转换为shared_ptr才能访问资源,若资源已释放(use_count==0),lock()返回空shared_ptr,避免悬空指针。
2. 底层实现关键
关联控制块:weak_ptr存储指向shared_ptr控制块的指针,仅跟踪weak_count:
c
template <typename T>
class weak_ptr {
public:
// 从shared_ptr构造:弱引用计数+1
weak_ptr(const shared_ptr<T>& other) {
control_block_ = other.control_block_;
if (control_block_) {
control_block_->weak_count++;
}
}
// 析构:弱引用计数-1,归零则释放控制块
~weak_ptr() {
if (control_block_) {
control_block_->weak_count--;
if (control_block_->use_count == 0 && control_block_->weak_count == 0) {
delete control_block_; // 资源和弱引用都归零,释放控制块
}
}
}
// 转换为shared_ptr(安全访问)
shared_ptr<T> lock() const {
if (control_block_ && control_block_->use_count > 0) {
return shared_ptr<T>(control_block_); // 引用计数+1
}
return shared_ptr<T>(); // 资源已释放,返回空
}
private:
ControlBlock* control_block_;
};
总结:
四、总结
智能指针 所有权 引用计数 拷贝性 典型场景
unique_ptr 独占 | 无 | 不可拷贝 | 单个对象 / 数组的独占管理
shared_ptr 共享 | 有 | 可拷贝 | 资源共享场景
weak_ptr 弱引用(无)| 无 | 可拷贝 | 解决循环引用