【c++11 之智能指针2 unique、shared、weak *_ptr 原理及案例】及四种智能指针对比分析

发布时间:2026/5/25 19:19:28

【c++11 之智能指针2 unique、shared、weak *_ptr 原理及案例】及四种智能指针对比分析 文章目录【全网最易懂】C11智能指针全解析unique_ptr/shared_ptr/weak_ptr原理实战避坑一、智能指针的核心价值二、三大智能指针核心原理从易到难2.1 std::unique_ptr独占式所有权最常用核心特性原理图解完整使用示例含最佳实践2.2 std::shared_ptr共享式所有权引用计数核心特性原理图解完整使用示例2.3 std::weak_ptr弱引用解决循环引用核心特性循环引用问题不使用weak_ptr的坑正确示例weak_ptr解决循环引用三、四大智能指针对比表含废弃auto_ptr四、最佳实践避坑指南4.1 优先使用make系列函数4.2 禁止的操作4.3 场景选择原则4.4 自定义删除器扩展五、手写简易智能指针理解底层总结【全网最易懂】C11智能指针全解析unique_ptr/shared_ptr/weak_ptr原理实战避坑一、智能指针的核心价值C11引入的智能指针本质是封装裸指针的模板类核心目标是自动管理动态内存生命周期——通过RAII资源获取即初始化机制在智能指针对象销毁时如离开作用域、析构自动调用delete释放内存彻底解决手动管理内存导致的内存泄漏、野指针、重复释放等问题。简单说智能指针 裸指针 自动释放 所有权管理。二、三大智能指针核心原理从易到难2.1 std::unique_ptr独占式所有权最常用核心特性独占所有权同一时间只能有一个unique_ptr指向同一个对象禁止拷贝/赋值避免多个指针管理同一内存移动语义可通过std::move转移所有权转移后原指针变为空轻量级无额外内存开销仅封装裸指针性能接近裸指针支持数组原生支持unique_ptrT[]自动调用delete[]。原理图解┌─────────────┐ 独占指向 ┌─────────────┐ │ unique_ptr │ ────────────────→ │ 动态对象 T │ └─────────────┘ └─────────────┘ (唯一所有者) (销毁时释放)完整使用示例含最佳实践#includememory#includeiostreamusingnamespacestd;classMyClass{public:MyClass(intval):value(val){coutMyClass(val) 构造\n;}~MyClass(){coutMyClass(value) 析构\n;}voidprint()const{cout值valueendl;}private:intvalue;};intmain(){// 推荐使用make_uniqueC14避免裸new更安全防止内存泄漏unique_ptrMyClassptr1make_uniqueMyClass(10);ptr1-print();// 输出值10// 禁止拷贝编译报错// unique_ptrMyClass ptr2 ptr1;// 允许移动转移所有权unique_ptrMyClassptr2move(ptr1);if(!ptr1){// 原指针变为空coutptr1已失去所有权\n;}ptr2-print();// 输出值10// 支持数组自动调用delete[]unique_ptrint[]arr_ptrmake_uniqueint[](5);arr_ptr[0]1;arr_ptr[1]2;cout数组元素arr_ptr[0], arr_ptr[1]endl;return0;// ptr2、arr_ptr销毁对应对象自动释放}输出结果MyClass(10) 构造 值10 ptr1已失去所有权 值10 数组元素1, 2 MyClass(10) 析构2.2 std::shared_ptr共享式所有权引用计数核心特性共享所有权多个shared_ptr可指向同一个对象引用计数内部维护一个计数器记录当前指向对象的shared_ptr数量拷贝/赋值时计数1指针销毁/重置时计数-1计数为0时自动释放对象线程安全引用计数的增减是原子操作但对象的访问仍需加锁。原理图解┌─────────────┐ │ shared_ptr1 │ ─┐ └─────────────┘ │ │ 引用计数2 ┌─────────────┐ ┌─────────────┐ │ ─────────────────→ │ 动态对象 T │ │ shared_ptr2 │ ─┘ └─────────────┘ └─────────────┘ (计数0时释放)完整使用示例#includememory#includeiostreamusingnamespacestd;classMyClass{public:MyClass(){coutMyClass 构造\n;}~MyClass(){coutMyClass 析构\n;}};intmain(){// 推荐make_sharedC11一次分配内存对象引用计数效率更高shared_ptrMyClassptr1make_sharedMyClass();cout当前引用计数ptr1.use_count()endl;// 输出1// 拷贝计数1shared_ptrMyClassptr2ptr1;cout当前引用计数ptr1.use_count()endl;// 输出2// 重置ptr1计数-1ptr1.reset();coutptr1重置后计数ptr2.use_count()endl;// 输出1return0;// ptr2销毁计数0对象析构}输出结果MyClass 构造 当前引用计数1 当前引用计数2 ptr1重置后计数1 MyClass 析构2.3 std::weak_ptr弱引用解决循环引用核心特性无所有权指向shared_ptr管理的对象但不增加引用计数观察作用仅用于“观察”对象是否存在不能直接访问对象解决循环引用两个shared_ptr互相引用时计数无法归0导致内存泄漏weak_ptr可打破该循环安全访问需通过lock()转换为shared_ptr后访问若对象已销毁lock()返回空。循环引用问题不使用weak_ptr的坑// 错误示例shared_ptr循环引用导致内存泄漏#includememory#includeiostreamusingnamespacestd;classA;classB;classA{public:shared_ptrBb_ptr;~A(){coutA 析构\n;}// 不会执行};classB{public:shared_ptrAa_ptr;~B(){coutB 析构\n;}// 不会执行};intmain(){shared_ptrAamake_sharedA();shared_ptrBbmake_sharedB();a-b_ptrb;// A引用Bb-a_ptra;// B引用A// 引用计数a2b2 → 函数结束后计数1对象无法释放return0;}输出结果无析构输出内存泄漏正确示例weak_ptr解决循环引用#includememory#includeiostreamusingnamespacestd;classA;classB;classA{public:shared_ptrBb_ptr;~A(){coutA 析构\n;}};classB{public:weak_ptrAa_wptr;// 改为weak_ptr不增加计数~B(){coutB 析构\n;}// 访问A对象的安全方法voidaccess_A(){shared_ptrAaa_wptr.lock();// 转换为shared_ptrif(a){cout成功访问A对象\n;}else{coutA对象已销毁\n;}}};intmain(){shared_ptrAamake_sharedA();shared_ptrBbmake_sharedB();a-b_ptrb;b-a_wptra;b-access_A();// 输出成功访问A对象return0;// a销毁→计数-1b销毁→计数-1 → 所有对象正常析构}输出结果成功访问A对象 A 析构 B 析构三、四大智能指针对比表含废弃auto_ptr特性auto_ptr (C98已废弃)unique_ptr (C11)shared_ptr (C11)weak_ptr (C11)所有权独有拷贝转移所有权独有禁止拷贝可移动共享引用计数无所有权仅观察拷贝/赋值隐式转移易出错禁止拷贝支持move赋值拷贝计数1赋值计数调整不支持拷贝/赋值内存开销无无引用计数额外内存指向引用计数无额外开销数组支持不支持原生支持unique_ptrT[]需自定义删除器不直接支持线程安全无仅自身对象访问需加锁计数原子操作对象需加锁依赖指向的shared_ptr典型场景已淘汰独占资源如局部对象共享资源如多模块访问解决shared_ptr循环引用安全性低隐式转移易崩溃高编译期检查高需避免循环引用高安全观察四、最佳实践避坑指南4.1 优先使用make系列函数make_uniqueC14/make_sharedC11避免裸new减少内存泄漏风险make_shared一次性分配对象引用计数内存效率更高示例auto ptr make_sharedMyClass();替代shared_ptrMyClass ptr(new MyClass());。4.2 禁止的操作不要用shared_ptr管理栈上对象析构时会调用delete导致未定义行为MyClass obj;shared_ptrMyClassptr(obj);// 错误obj销毁时会被重复释放不要将同一个裸指针交给多个智能指针管理重复释放weak_ptr必须通过lock()转换为shared_ptr后再访问对象直接解引用崩溃。4.3 场景选择原则能使用unique_ptr的场景绝不使用shared_ptrunique_ptr更轻量需共享资源时使用shared_ptr但警惕循环引用配合weak_ptrweak_ptr仅作为shared_ptr的补充不单独使用。4.4 自定义删除器扩展智能指针支持自定义删除器用于管理非内存资源如文件句柄、网络连接// 示例shared_ptr管理文件句柄#includememory#includecstdiousingnamespacestd;// 自定义删除器关闭文件autofile_deleter[](FILE*fp){if(fp)fclose(fp);printf(文件已关闭\n);};intmain(){shared_ptrFILEfp(fopen(test.txt,w),file_deleter);if(fp)fprintf(fp.get(),Hello Smart Pointer\n);return0;// fp销毁自动调用删除器关闭文件}五、手写简易智能指针理解底层以unique_ptr为例实现核心RAII逻辑#includeiostreamusingnamespacestd;// 简易unique_ptr模板templatetypenameTclassMyUniquePtr{public:// 构造接管裸指针explicitMyUniquePtr(T*ptrnullptr):m_ptr(ptr){}// 禁止拷贝核心独占所有权MyUniquePtr(constMyUniquePtr)delete;MyUniquePtroperator(constMyUniquePtr)delete;// 移动构造转移所有权MyUniquePtr(MyUniquePtrother)noexcept{m_ptrother.m_ptr;other.m_ptrnullptr;}// 移动赋值MyUniquePtroperator(MyUniquePtrother)noexcept{if(this!other){deletem_ptr;// 释放当前资源m_ptrother.m_ptr;other.m_ptrnullptr;}return*this;}// 析构自动释放~MyUniquePtr(){deletem_ptr;cout自定义智能指针资源已释放\n;}// 重载-和*模拟指针行为T*operator-()const{returnm_ptr;}Toperator*()const{return*m_ptr;}// 判空explicitoperatorbool()const{returnm_ptr!nullptr;}private:T*m_ptr;};// 测试classTest{public:Test(){coutTest 构造\n;}~Test(){coutTest 析构\n;}voidprint(){coutTest print\n;}};intmain(){MyUniquePtrTestptr(newTest());ptr-print();// 输出Test printreturn0;}输出结果Test 构造 Test print Test 析构 自定义智能指针资源已释放总结C11智能指针的核心是RAII机制unique_ptr独占资源shared_ptr共享资源引用计数weak_ptr解决循环引用优先使用make_unique/make_shared创建智能指针避免裸newunique_ptr是首选轻量、安全shared_ptr仅在需共享资源时使用weak_ptr仅作为shared_ptr的补充杜绝使用auto_ptr其隐式所有权转移是设计缺陷已被unique_ptr完全替代。

相关新闻