跳到主要内容 C++ 智能指针原理、使用场景与避坑指南 | 极客日志
C++ 算法
C++ 智能指针原理、使用场景与避坑指南 解析 C++ 智能指针的核心原理与使用。基于 RAII 思想,介绍 unique_ptr、shared_ptr、weak_ptr 等标准库实现及自定义删除器。涵盖循环引用、线程安全等常见问题解决方案,并提供最佳实践建议,帮助开发者避免内存泄漏,提升代码安全性。
深海蔚蓝 发布于 2026/3/23 更新于 2026/4/16 28K 浏览在 C++ 编程中,内存管理始终是核心挑战之一。手动使用 new 分配内存后,若因异常、逻辑疏忽等原因未执行 ,就会导致内存泄漏。智能指针作为 C++ 的核心工具,通过 RAII(资源获取即初始化)思想自动管理资源,完美解决了这一痛点。本文将从使用场景、设计原理、标准库实现、常见问题等方面,全面解析智能指针的技术细节。
delete
一、智能指针的核心使用场景 手动管理动态内存时,异常场景下的资源释放问题尤为突出。看下面的示例代码:
double Divide (int a, int b) {
if (b == 0 ) throw "Divide by zero condition!" ;
return (double )a / (double )b;
}
void Func () {
int * array1 = new int [10 ];
int * array2 = new int [10 ];
try {
int len, time;
cin >> len >> time;
cout << Divide (len, time) << endl;
} catch (...) {
delete [] array1;
delete [] array2;
throw ;
}
delete [] array1;
delete [] array2;
}
上述代码存在两个问题:一是 array2 初始化时若抛异常,array1 会泄漏;二是嵌套异常处理导致代码冗余。而智能指针能自动管理资源生命周期,无论正常执行还是异常退出,都会在对象析构时释放资源,让代码更简洁、安全。
二、智能指针的设计基石:RAII 思想
2.1 RAII 核心原理 RAII(Resource Acquisition Is Initialization)即'资源获取即初始化',是一种通过对象生命周期管理资源的设计思想。其核心逻辑如下:
资源获取:在对象构造时获取资源(如动态内存、文件句柄、网络连接等);
资源持有:对象生命周期内始终持有资源,确保资源有效;
资源释放:对象析构时自动释放资源,无需手动干预。
2.2 自定义智能指针示例 基于 RAII 思想,我们可以实现一个简单的智能指针,重载指针操作符以模拟原生指针行为:
template <class T >
class SmartPtr {
public :
SmartPtr (T* ptr) : _ptr(ptr) {}
~SmartPtr () {
cout << "delete[] " << _ptr << endl;
delete [] _ptr;
}
T& operator *() { return *_ptr; }
T* operator ->() { return _ptr; }
T& operator [](size_t i) { return _ptr[i]; }
private :
T* _ptr;
};
使用该智能指针重构 Func 函数,代码将大幅简化:
void Func () {
SmartPtr<int > sp1 = new int [10 ];
SmartPtr<int > sp2 = new int [10 ];
for (size_t i = 0 ; i < 10 ; i++) {
sp1[i] = sp2[i] = i;
}
int len, time;
cin >> len >> time;
cout << Divide (len, time) << endl;
}
无论是否抛出异常,sp1和sp2析构时都会自动释放数组,彻底避免内存泄漏。
三、C++ 标准库智能指针详解 C++ 标准库在 <memory> 头文件中提供了 4 种智能指针,各自适用于不同场景,核心差异在于资源所有权管理策略。
3.1 auto_ptr(已废弃)
特性:C++98 引入的第一代智能指针,拷贝时转移资源所有权(被拷贝对象悬空);
缺陷:设计缺陷导致访问悬空指针崩溃,C++11 后被强烈建议废弃;
auto_ptr<Date> ap1 (new Date) ;
auto_ptr<Date> ap2 (ap1) ;
3.2 unique_ptr(独占指针)
特性:C++11 引入,独占资源所有权 ,不支持拷贝,仅支持移动(move);
适用场景:无需共享资源的场景(最常用的智能指针);
关键特性:
禁用拷贝构造和赋值运算符(= delete);
支持移动语义(std::move),移动后原对象悬空;
特化支持 new[] 资源(unique_ptr<Date[]> up(new Date[5]));
unique_ptr<Date> up1 (new Date) ;
unique_ptr<Date> up3 (move(up1)) ;
3.3 shared_ptr(共享指针)
特性:C++11 引入,支持资源共享 ,通过引用计数管理资源生命周期;
核心原理:
堆上维护引用计数(多个对象共享同一计数);
拷贝时计数 + 1,析构时计数 - 1;
计数为 0 时,释放资源;
适用场景:需要多个对象共享资源的场景;
关键特性:
支持 make_shared 构造(更高效,避免内存碎片);
支持自定义删除器(处理非 new 分配的资源);
特化支持 new[] 资源;
shared_ptr<Date> sp1 (new Date) ;
shared_ptr<Date> sp2 (sp1) ;
shared_ptr<Date> sp3 = make_shared <Date>(2024 , 9 , 11 );
cout << sp1. use_count () << endl;
3.4 weak_ptr(弱指针)
特性:C++11 引入,不管理资源,仅作为 shared_ptr 的辅助工具 ;
核心作用:解决 shared_ptr 的循环引用问题;
关键特性:
绑定 shared_ptr 时不增加引用计数;
无 operator* 和 operator->,需通过 lock() 获取 shared_ptr 访问资源;
支持 expired() 检查资源是否已释放;
shared_ptr<string> sp1 = make_shared <string>("hello" );
weak_ptr<string> wp = sp1;
if (!wp.expired ()) {
shared_ptr<string> sp2 = wp.lock ();
cout << *sp2 << endl;
}
3.5 自定义删除器 智能指针默认使用 delete 释放资源,若资源通过 new[]、fopen 等方式获取,需自定义删除器:
template <class T > class DeleteArray {
public :
void operator () (T* ptr) { delete [] ptr; }
};
shared_ptr<FILE> sp5 (fopen("test.cpp" , "r" ), [](FILE* ptr) { fclose(ptr); }) ;
shared_ptr<Date> sp2 (new Date[5 ], DeleteArray<Date>()) ;
unique_ptr<Date, DeleteArray<Date>> up2 (new Date[5 ]);
四、智能指针的核心原理实现
4.1 unique_ptr 实现(核心:禁用拷贝) template <class T >
class unique_ptr {
public :
explicit unique_ptr (T* ptr = nullptr ) : _ptr(ptr) { }
unique_ptr (unique_ptr<T>&& sp) : _ptr(sp._ptr) { sp._ptr = nullptr ; }
~unique_ptr () { if (_ptr) delete _ptr; }
unique_ptr (const unique_ptr<T>&) = delete ;
unique_ptr<T>& operator =(const unique_ptr<T>&) = delete ;
T& operator *() { return *_ptr; }
T* operator ->() { return _ptr; }
private :
T* _ptr;
};
4.2 shared_ptr 实现(核心:引用计数) template <class T >
class shared_ptr {
public :
explicit shared_ptr (T* ptr = nullptr ) : _ptr(ptr), _pcount(new int(1 )) { }
shared_ptr (const shared_ptr<T>& sp) : _ptr(sp._ptr), _pcount(sp._pcount) { ++(*_pcount); }
~shared_ptr () { if (--(*_pcount) == 0 ) { delete _ptr; delete _pcount; } }
T& operator *() { return *_ptr; }
T* operator ->() { return _ptr; }
int use_count () const { return *_pcount; }
private :
T* _ptr;
int * _pcount;
function<void (T*)> _del = [](T* p) { delete p; };
};
五、智能指针的常见问题与解决方案
5.1 shared_ptr 循环引用(致命问题)
问题场景 两个 shared_ptr 相互引用,导致引用计数无法归零,资源泄漏:
struct ListNode {
int _data;
shared_ptr<ListNode> _next;
shared_ptr<ListNode> _prev;
~ListNode () { cout << "~ListNode()" << endl; }
};
int main () {
shared_ptr<ListNode> n1 (new ListNode) ;
shared_ptr<ListNode> n2 (new ListNode) ;
n1->_next = n2;
n2->_prev = n1;
return 0 ;
}
解决方案 将相互引用的成员改为 weak_ptr(不增加引用计数):
struct ListNode {
int _data;
weak_ptr<ListNode> _next;
weak_ptr<ListNode> _prev;
~ListNode () { cout << "~ListNode()" << endl; }
};
5.2 shared_ptr 线程安全问题
问题说明
引用计数的增减操作不是原子的,多线程拷贝 / 析构 shared_ptr 会导致计数错乱;
shared_ptr 指向的对象本身线程不安全,需手动加锁保护。
解决方案
引用计数使用原子类型(atomic<int>*)或加锁;
访问共享对象时,使用互斥锁(mutex)保护。
5.3 避免悬空指针
unique_ptr 和 auto_ptr 移动后,原对象悬空,禁止后续访问;
weak_ptr 需通过 expired() 检查资源状态,再通过 lock() 获取 shared_ptr 访问。
六、智能指针的最佳实践
优先使用 unique_ptr :无需共享资源时,unique_ptr 效率最高(无引用计数开销);
共享资源用 shared_ptr :使用 make_shared 构造(比直接 new 更高效);
避免循环引用 :相互引用的场景用 weak_ptr;
自定义删除器 :非 new 分配的资源(如 new[]、文件句柄)必须指定删除器;
禁止隐式转换 :shared_ptr 和 unique_ptr 的构造函数用 explicit 修饰,避免普通指针隐式转换;
避免手动管理资源 :尽量用智能指针替代 new/delete,从源头避免内存泄漏。
七、总结 智能指针是 C++ 内存管理的核心工具,其本质是 RAII 思想的实现。unique_ptr 和 shared_ptr 覆盖了绝大多数场景,weak_ptr 则解决了 shared_ptr 的循环引用问题。掌握智能指针的使用场景、原理和避坑要点,能大幅提升代码的安全性和可维护性。在实际开发中,应优先使用标准库智能指针,避免手动管理动态内存,从根本上杜绝内存泄漏。
微信扫一扫,关注极客日志 微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
相关免费在线工具 加密/解密文本 使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
Base64 字符串编码/解码 将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
Base64 文件转换器 将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
Markdown转HTML 将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online
HTML转Markdown 将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online
JSON 压缩 通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online