吐血推荐9个一键生成论文工具,本科生毕业论文轻松搞定!
2026/1/16 16:26:20
| 特性 | std::unique_ptr | std::shared_ptr | std::weak_ptr |
|---|---|---|---|
| 所有权 | 独占所有权 | 共享所有权 | 无所有权(弱引用) |
| 拷贝语义 | 不可拷贝,只能移动 | 可拷贝,引用计数增加 | 可拷贝,不增加引用计数 |
| 资源释放时机 | 所有者销毁时 | 最后一个shared_ptr销毁时 | 不负责释放资源 |
| 性能开销 | 几乎零开销(与裸指针相当) | 引用计数开销(原子操作) | 引用计数开销 |
| 内存布局 | 单个指针大小 | 两个指针大小(对象指针+控制块指针) | 单个指针大小 |
| 线程安全 | 非线程安全(需外部同步) | 引用计数操作线程安全,对象访问需同步 | 引用计数操作线程安全 |
std::unique_ptr设计哲学:独占所有权,零开销抽象
// 独占所有权,不能共享std::unique_ptr<int>ptr1=std::make_unique<int>(42);// std::unique_ptr<int> ptr2 = ptr1; // 错误!不能拷贝std::unique_ptr<int>ptr2=std::move(ptr1);// 只能移动转移所有权// 自定义删除器(编译时确定)autodeleter=[](int*p){std::cout<<"Deleting int\n";deletep;};std::unique_ptr<int,decltype(deleter)>ptr3(newint(10),deleter);// 数组版本std::unique_ptr<int[]>arr=std::make_unique<int[]>(10);arr[0]=1;// 支持operator[]优点:
缺点:
std::shared_ptr设计哲学:共享所有权,引用计数
#include<memory>#include<iostream>classResource{public:Resource(){std::cout<<"Resource created\n";}~Resource(){std::cout<<"Resource destroyed\n";}};voidusage(){// 创建shared_ptrautosp1=std::make_shared<Resource>();// 引用计数: 1{autosp2=sp1;// 拷贝,引用计数: 2autosp3=sp1;// 拷贝,引用计数: 3std::cout<<"sp1.use_count() = "<<sp1.use_count()<<"\n";// 3}// sp2, sp3销毁,引用计数: 1// 控制块包含:引用计数、弱引用计数、删除器、分配器等std::cout<<"sp1.use_count() = "<<sp1.use_count()<<"\n";// 1}// sp1销毁,引用计数: 0,资源被释放// 循环引用问题示例structNode{std::shared_ptr<Node>next;~Node(){std::cout<<"Node destroyed\n";}};voidcircularReference(){autonode1=std::make_shared<Node>();autonode2=std::make_shared<Node>();node1->next=node2;// 引用计数: node1=1, node2=2node2->next=node1;// 引用计数: node1=2, node2=2 - 循环引用!// 函数结束时,引用计数都变为1,内存泄漏!}// 自定义删除器(运行时确定)voidcustomDeleter(int*p){std::cout<<"Custom delete\n";deletep;}autosp=std::shared_ptr<int>(newint(42),customDeleter);优点:
缺点:
std::weak_ptr设计哲学:弱引用,避免所有权
classObserver;classSubject{std::vector<std::weak_ptr<Observer>>observers;public:voidaddObserver(std::weak_ptr<Observer>obs){observers.push_back(obs);}voidnotify(){for(auto&weakObs:observers){if(autoobs=weakObs.lock()){// 尝试获取shared_ptr// 如果对象还存在,通知它obs->onNotify();}else{// 对象已被释放,可以清理弱引用}}}};classObserver{public:voidonNotify(){std::cout<<"Notified!\n";}};// 使用示例voidobserverPattern(){autosubject=std::make_shared<Subject>();autoobserver=std::make_shared<Observer>();subject->addObserver(observer);// 传递weak_ptr// observer可以在其他地方被释放,不会影响subjectobserver.reset();// 释放observersubject->notify();// 安全,不会访问已释放的对象}// 解决循环引用structSafeNode{std::weak_ptr<SafeNode>next;// 使用weak_ptr避免循环引用~SafeNode(){std::cout<<"SafeNode destroyed\n";}};voidnoCircularReference(){autonode1=std::make_shared<SafeNode>();autonode2=std::make_shared<SafeNode>();node1->next=node2;// weak_ptr不增加引用计数node2->next=node1;// 引用计数保持为1// 函数结束时,两个节点都能正确释放}优点:
lock()检查)缺点:
lock())#include<memory>#include<chrono>#include<iostream>constintITERATIONS=10000000;voidtestUniquePtr(){autostart=std::chrono::high_resolution_clock::now();for(inti=0;i<ITERATIONS;++i){autoptr=std::make_unique<int>(i);// 移动操作autoptr2=std::move(ptr);}autoend=std::chrono::high_resolution_clock::now();std::chrono::duration<double>diff=end-start;std::cout<<"unique_ptr: "<<diff.count()<<"s\n";}voidtestSharedPtr(){autostart=std::chrono::high_resolution_clock::now();for(inti=0;i<ITERATIONS;++i){autoptr=std::make_shared<int>(i);autoptr2=ptr;// 拷贝,引用计数操作autoptr3=ptr;// 再次拷贝}autoend=std::chrono::high_resolution_clock::now();std::chrono::duration<double>diff=end-start;std::cout<<"shared_ptr: "<<diff.count()<<"s\n";}// 典型结果(10,000,000次操作):// unique_ptr: 0.15s// shared_ptr: 1.20s (约8倍慢)内存布局对比:
structObject{intdata[100];};// unique_ptr: 一个指针(8字节)std::unique_ptr<Object>up=std::make_unique<Object>();// shared_ptr: 两个指针(16字节)+ 控制块(~32字节)std::shared_ptr<Object>sp=std::make_shared<Object>();// make_shared将对象和控制块分配在一起,提高局部性// shared_ptr<Object> sp(new Object()); // 分离分配,较差std::unique_ptr的场景:// 1. 工厂函数返回std::unique_ptr<Database>createDatabase(){returnstd::make_unique<MySQLDatabase>();}// 2. 独占资源管理classConnection{std::unique_ptr<Socket>socket;// 独占socketstd::unique_ptr<Buffer>buffer;// 独占buffer};// 3. Pimpl惯用法classMyClass{structImpl;std::unique_ptr<Impl>pimpl;};// 4. 容器中的对象std::vector<std::unique_ptr<Shape>>shapes;shapes.push_back(std::make_unique<Circle>());std::shared_ptr的场景:// 1. 共享缓存classCache{staticstd::unordered_map<std::string,std::shared_ptr<Resource>>cache;staticstd::shared_ptr<Resource>get(conststd::string&key){returncache[key];// 共享所有权}};// 2. 共享配置classAppConfig{std::shared_ptr<Config>config;// 多个组件共享同一配置};// 3. 需要延长对象生命周期的回调classAsyncOperation{std::shared_ptr<Callback>callback;// 确保回调对象在操作完成前存活};// 4. 多线程共享数据classThreadPool{std::shared_ptr<TaskQueue>queue;// 多个工作线程共享任务队列};std::weak_ptr的场景:// 1. 打破循环引用classParent{std::shared_ptr<Child>child;};classChild{std::weak_ptr<Parent>parent;// 使用weak_ptr};// 2. 观察者模式classSubject{std::vector<std::weak_ptr<Observer>>observers;};// 3. 缓存实现classCache{std::unordered_map<std::string,std::weak_ptr<Resource>>weakCache;std::shared_ptr<Resource>get(conststd::string&key){if(autoit=weakCache.find(key);it!=weakCache.end()){if(autoresource=it->second.lock()){returnresource;// 对象还存在}weakCache.erase(it);// 对象已释放,清理}// 重新加载...}};// 4. 临时对象引用classProcessor{std::weak_ptr<TempData>tempData;// 临时数据,可能被提前释放};优先使用std::unique_ptr
谨慎使用std::shared_ptr
std::make_shared合理使用std::weak_ptr
lock()检查有效性选择原则:
unique_ptrshared_ptrweak_ptr性能考虑:
shared_ptrunique_ptr或直接存储shared_ptr// 混合使用示例classSystem{private:std::unique_ptr<Database>db;// 独占数据库连接std::shared_ptr<Config>config;// 共享配置std::weak_ptr<Monitor>monitor;// 弱引用监控器(可能不存在)std::vector<std::unique_ptr<Task>>tasks;// 独占任务对象std::shared_ptr<Logger>logger;// 共享日志器};