一 unique_ptr独占智能指针
独占所有权,同一时间只能有一个unique_ptr指向这块堆内存,禁止拷贝,只允许移动。
不能拷贝:unqiue_ptr<int> p2 = p1;编译报错;
可以移动:unique_ptr<int> p2 = move(p1);移动后p1变成空,p2接管资源;
生命周期:出作用域,自动调用delete释放内存;
示例代码:
#include <iostream> #include <memory> using namespace std; int main() { // 创建unique_ptr,管理堆上int unique_ptr<int> p1(new int(100)); cout << *p1 << endl; // unique_ptr<int> p2 = p1; // ❌ 编译报错!禁止拷贝,独占 unique_ptr<int> p2 = move(p1); // ✅ 移动所有权 cout << *p2 << endl; // p1 现在是空指针,不能再使用*p1 return 0; } // 出作用域,p2销毁,自动释放堆内存,不用手动delete要点:
1.底层:析构的时候直接释放内存,没有引用计数,开销很小;
2.适用场景:资源只需要一个管理者(绝大多数场景优先用unique_ptr);
3.C++14推荐写法:auto p = make_unique<int>(100);
二 shared_ptr共享智能指针
共享所有权,维护引用计数;每多一个shared_ptr指向同一块内存,计数+1;对象析构计数-1;引用计数减到0,释放堆内存。
引用计数:单独存放在堆上,不是存在对象内部;
拷贝允许:拷贝时引用计数+1;
移动:原指针置空,计数不变;
示例代码:
#include <iostream> #include <memory> using namespace std; int main() { shared_ptr<int> p1 = make_shared<int>(200); // 计数=1 cout << p1.use_count() << endl; // use_count查看引用计数 shared_ptr<int> p2 = p1; // ✅ 允许拷贝,共享资源,计数=2 cout << p2.use_count() << endl; p1.reset(); // p1放弃管理,计数=1 cout << p2.use_count() << endl; return 0; } // 函数结束p2析构,计数变成0,释放内存致命问题:循环引用(内存泄漏)
两个对象A,B,A里面存shared_ptr<B>,B里面存shared_ptr A持有B,B持有A。两个对象析构的时候,引用计数都减到1,永远不为0,内存永远不释放。
struct B; struct A{ shared_ptr<B> b_ptr; ~A(){cout << "A析构"<<endl;} }; struct B{ shared_ptr<A> a_ptr; ~B(){cout << "B析构"<<endl;} }; int main() { shared_ptr<A> a = make_shared<A>(); shared_ptr<B> b = make_shared<B>(); a->b_ptr = b; b->a_ptr = a; // 出main,a、b析构,但是内部互相持有,引用计数=1 // A、B的析构函数**不会执行,内存泄漏!** return 0; }运行结果:不会打印A析构,B析构,内存泄漏;
解决方案:把其中一个shared_ptr改为weak_ptr;
三 weak_ptr弱引用
不增加shared_ptr的引用计数,不拥有资源,只是“旁观”shared_ptr管理的对象
weak_ptr不能直接解引用访问对象,必须调用.lock(),返回一个临时shared_ptr;
lock()底层是如何操作的
拿到 weak_ptr 保存的控制块地址(weak_ptr 内部只存控制块指针 + 对象指针,不持有对象)
原子操作读取 use_count,判断
use_count > 0如果 use_count == 0:对象已经被释放 → 直接返回空的
shared_ptr如果 use_count > 0:尝试原子地把 use_count +=1(原子!防止多线程并发释放)
构造一个新的 shared_ptr,把对象指针、控制块指针交给这个新 shared_ptr 返回
整个过程是原子的,防止多线程下 “判断完还没加计数,对象就被别的线程释放”
可以用.expired()判断资源是否已经被释放;
专门解决shared_ptr循环引用问题;
修复上面循环引用代码(把A内部的shared_ptr<B>改为weak_ptr<B>)
#include <iostream> #include <memory> using namespace std; struct B; struct A{ weak_ptr<B> b_ptr; // 弱指针!不增加引用计数 ~A(){cout << "A析构"<<endl;} }; struct B{ shared_ptr<A> a_ptr; ~B(){cout << "B析构"<<endl;} }; int main() { shared_ptr<A> a = make_shared<A>(); shared_ptr<B> b = make_shared<B>(); a->b_ptr = b; // weak_ptr赋值,b的引用计数不会+1 b->a_ptr = a; return 0; } // 输出:A析构 B析构,内存正常释放!当main函数结束:
1.b析构,B内部a_ptr计数减1,a的引用计数变成0->A对象释放;
2.A释放后,weak_ptr自动失效,没有指针再持有B,B的引用计数变成0->B释放。
使用weak_ptr访问对象示例:
shared_ptr<int> s = make_shared<int>(666); weak_ptr<int> w = s; // cout << *w; // ❌ weak_ptr不能直接解引用 shared_ptr<int> tmp = w.lock(); // 拿到临时shared_ptr if(tmp != nullptr) { cout << *tmp << endl; }