本文共 11086 字,大约阅读时间需要 36 分钟。
C++没有内存回收机制,每次程序员new出来的对象需要手动delete,流程复杂时可能会漏掉delete,导致内存泄漏。于是C++引入智能指针,可用于动态资源管理,资源即对象的管理策略。
使用 raw pointer 管理动态内存时,经常会遇到这样的问题:
- 忘记
delete
内存,造成内存泄露。 - 出现异常时,不会执行
delete
,造成内存泄露。
智能指针主要有三种:shared_ptr,unique_ptr和weak_ptr。
shared_ptr
shared_ptr是最常用的智能指针(项目中我只用过shared_ptr)。shared_ptr采用了引用计数器,多个shared_ptr中的T *ptr指向同一个内存区域(同一个对象),并共同维护同一个引用计数器。shared_ptr定义如下,记录同一个实例被引用的次数,当引用次数大于0时可用,等于0时释放内存。
注意避免循环引用,shared_ptr的一个最大的陷阱是循环引用,循环,循环引用会导致堆内存无法正确释放,导致内存泄漏。循环引用在weak_ptr中介绍。引用传递不加1;
shared_ptr对象每次离开作用域时会自动调用析构函数,而析构函数并不像其他类的析构函数一样,而是在释放内存是先判断引用计数器是否为0。等于0才做delete操作,否则只对引用计数器左减一操作。
接下来看一下构造函数,默认构造函数的引用计数器为0,ptr指向NULL:
SharedPtr() : _ptr((T *)0), _refCount(0){}~SharedPtr(){ if (_ptr && --*_refCount == 0) { delete _ptr; delete _refCount; }}
用普通指针初始化智能指针时,引用计数器初始化为1:
SharedPtr(T *obj) : _ptr(obj), _refCount(new int(1)){}
赋值运算符的重载
当用一个shared_ptr<T> other去给另一个 shared_ptr<T> sp赋值时,发生了两件事情:
一、sp指针指向发生变化,不再指向之前的内存区域,所以赋值前原来的_refCount要自减
二、sp指针指向other.ptr,所以other的引用计数器_refCount要做++操作。
templateclass shared_ptr;
Member functions(constructor)Construct shared_ptr (public member function )(destructor)Destroy shared_ptr (public member function )operator=shared_ptr assignment (public member function )swapSwap content (public member function )resetReset pointer (public member function )getGet pointer (public member function )operator*Dereference object (public member function )operator->Dereference object member (public member function )use_countUse count (public member function )uniqueCheck if unique (public member function )operator boolCheck if not null (public member function )owner_beforeOwner-based ordering (public member function template )Non-member functionsOverloads:swapExchange content of shared_ptr objects (function template )relational operatorsRelational operators ==, !=, <, <=, >, >= (function template )ostream operator<
实例:
#include "shared_ptr.h"//class Foo {//public:// Foo() { std::cout << "Foo...\n"; }// ~Foo() { std::cout << "~Foo...\n"; }//};////struct D {// void operator()(Foo* p) const {// std::cout << "Call delete from function object...\n";// delete p;// }//};void func_shared_ptr(const shared_ptr foo_){ cout << "foo_.use_count = " << foo_.use_count() << endl;}int stl_shared_ptr(){ std::shared_ptr p1; { std::cout << "use_count:\n"; std::cout << "p1: " << p1.use_count() << '\n'; p1.reset(new int(0)); std::cout << "p1: " << p1.use_count() << '\n'; std::shared_ptr p2(nullptr); std::cout << "p2: " << p2.use_count() << '\n'; std::shared_ptr p3(new int); std::cout << "p3: " << p3.use_count() << '\n'; std::shared_ptr p4(new int, std::default_delete ());//给出析构函数 std::cout << "p4: " << p4.use_count() << '\n'; std::shared_ptr p5(new int, [](int* p){delete p; }, std::allocator ());//给出构造函数和析构函数 std::cout << "p5: " << p5.use_count() << '\n'; { std::shared_ptr p6(p5);//调用拷贝构造函数 p6原本的减一,p5加一 std::cout << "p5in: " << p5.use_count() << '\n'; std::cout << "p6in: " << p6.use_count() << '\n'; } std::cout << "p5: " << p5.use_count() << '\n'; std::shared_ptr p6(p5); std::shared_ptr p7(std::move(p6));//左值变右值,p6失去对指针的权力 std::cout << "p5: " << p5.use_count() << '\n'; std::cout << "p6: " << p6.use_count() << '\n'; std::cout << "p7: " << p7.use_count() << '\n'; std::shared_ptr p8(std::unique_ptr (new int)); std::cout << "p8: " << p8.use_count() << '\n'; std::shared_ptrobj(new C); std::shared_ptr p9(obj, obj->data);//构造 std::cout << "p9: " << p9.use_count() << '\n'; } std::cout << "p3: " << p1.use_count() << '\n'; //考察一下引用传递对共享次数 指针的传递就会增加引用计数。 { auto foo_s_ptr = make_shared (12);// 注意maked的使用 shared_ptr foo_s_ptr2(foo_s_ptr); cout << "foo_s_ptr.use_count = " << foo_s_ptr.use_count() << endl; cout << "foo_s_ptr2.use_count = " << foo_s_ptr2.use_count() << endl; shared_ptr foo_s_ptr3(foo_s_ptr); cout << "foo_s_ptr.use_count = " << foo_s_ptr.use_count() << endl; func_shared_ptr(foo_s_ptr2); cout << "foo_s_ptr.use_count = " << foo_s_ptr.use_count() << endl; } //类型 { std::cout << "constructor with no managed object\n"; std::shared_ptr sh1; } //调用构造函数 { std::cout << "constructor with object\n"; std::shared_ptr sh2(new Foo); std::shared_ptr sh3(sh2); std::cout << sh2.use_count() << '\n'; std::cout << sh3.use_count() << '\n';} //注册deleter { std::cout << "constructor with object and deleter\n"; std::shared_ptr sh4(new Foo, D()); std::shared_ptr sh5(new Foo, [](Foo *p) { std::cout << "Call delete from lambda...\n"; delete p; }); } return 1;}
Class unique_ptr
不共享它的指针。它无法复制到其他 unique_ptr,无法通过值传递到函数,也无法用于需要副本的任何标准模板库 (STL) 算法。只能移动unique_ptr。这意味着,内存资源所有权将转移到另一 unique_ptr,并且原始 unique_ptr 不再拥有此资源。我们建议你将对象限制为由一个所有者所有,因为多个所有权会使程序逻辑变得复杂。因此,当需要智能指针用于纯 C++ 对象时,可使用 unique_ptr,而当构造 unique_ptr 时,可使用 Helper 函数。
拷贝一个std::unique_ptr将不被允许,因为如果你拷贝一个std::unique_ptr,那么拷贝结束后,这两个std::unique_ptr都会指向相同的资源,它们都认为自己拥有这块资源(所以都会企图释放)。
因此std::unique_ptr是一个仅能移动(move_only)的类型。
1.创建
unique_ptr不像shared_ptr一样拥有标准库函数make_shared来创建一个shared_ptr实例。要想创建一个unique_ptr,我们需要将一个new 操作符返回的指针传递给unique_ptr的构造函数。
int main(){ // 创建一个unique_ptr实例 unique_ptr pInt(new int(5)); cout << *pInt;}
2、无法进行复制构造和赋值操作
unique_ptr没有copy构造函数,不支持普通的拷贝和赋值操作。
int main() { // 创建一个unique_ptr实例 unique_ptr pInt(new int(5)); unique_ptr pInt2(pInt); // 报错 unique_ptr pInt3 = pInt; // 报错}
3、可以进行移动构造和移动赋值操作
unique_ptr虽然没有支持普通的拷贝和赋值操作,但却提供了一种移动机制来将指针的所有权从一个unique_ptr转移给另一个unique_ptr。如果需要转移所有权,可以使用std::move()函数。
int main() { unique_ptr pInt(new int(5)); unique_ptr pInt2 = std::move(pInt); // 转移所有权 //cout << *pInt << endl; // 出错,pInt为空 cout << *pInt2 << endl; unique_ptr pInt3(std::move(pInt2));}
4、可以返回unique_ptr
unique_ptr不支持拷贝操作,但却有一个例外:可以从函数中返回一个unique_ptr。
unique_ptr clone(int p){ unique_ptr pInt(new int(p)); return pInt; // 返回unique_ptr}int main() { int p = 5; unique_ptr ret = clone(p); cout << *ret << endl;}
5.unique_ptr使用场景
为动态申请的资源提供异常安全保证
返回函数内动态申请资源的所有权
在容器中保存指针
管理动态数组
作为auto_ptr的替代品
实例
#include "unique_ptr.h"#include#include using namespace std;void f(const unique_Foo &){ std::cout << "f(const unique_Foo&)\n";}void TestAutoDestroy(){ //1. 普通的new对象. std::cout << "TestDestroy...................." << std::endl; { std::unique_ptr p1(new unique_Foo); } //2. 普通的new[]对象. { std::unique_ptr p2(new unique_Foo[4]);//unique_ptr数组模式 } //3. 自定义的deleter.和shared_ptr类似可以自己写一个析构函数,有多种方式 { std::unique_ptr p3(new unique_Foo); std::unique_ptr > p4(new unique_Foo, [](unique_Foo *p){ delete p; });//类似的lambda 表达式 }}void TestOwner(){ std::cout << "TestOwner...................." << std::endl; //1. new object. std::unique_ptr p1(new unique_Foo); // p1 owns unique_Foo if (p1) p1->bar(); { std::unique_ptr p2(std::move(p1)); // now p2 owns unique_Foo f(*p2); p1 = std::move(p2); // ownership returns to p1 p2->bar(); std::cout << "destroying p2...\n"; } p1->bar();}void TestArrayOwner(){ std::cout << "TestArrayOwner...................." << std::endl; //1. new[] object. std::unique_ptr p1(new unique_Foo[4]); // p1 owns unique_Foo if (p1) p1[0].bar(); { std::unique_ptr p2(std::move(p1)); // now p2 owns unique_Foo f(p2[0]); p1 = std::move(p2); // ownership returns to p1 p2[0].bar(); std::cout << "destroying p2...\n"; } p1[0].bar();}int stl_unique_ptr(){ TestAutoDestroy(); TestOwner(); TestArrayOwner(); return 0;}
weak_ptr
weak_ptr 是一种不控制对象生命周期的智能指针, 它指向一个 shared_ptr 管理的对象. 进行该对象的内存管理的是那个强引用的 shared_ptr. weak_ptr只是提供了对管理对象的一个访问手段.
weak_ptr 设计的目的是为配合 shared_ptr 而引入的一种智能指针来协助 shared_ptr 工作, 它只可以从一个 shared_ptr 或另一个 weak_ptr 对象构造, 它的构造和析构不会引起引用记数的增加或减少.std::weak_ptr
是一种智能指针,它对被 管理的对象存在非拥有性(“弱”)引用。在访问所引用的对象前必须先转换为 。
std::weak_ptr
用来表达临时所有权的概念:当某个对象只有存在时才需要被访问,而且随时可能被他人删除时,可以使用 std::weak_ptr
来跟踪该对象。需要获得临时所有权时,则将其转换为 ,此时如果原来的 被销毁,则该对象的生命期将被延长至这个临时的 同样被销毁为止。
此外,std::weak_ptr
还可以用来避免 的循环引用。
weak_ptr 在功能上类似于普通指针, 然而一个比较大的区别是, 弱引用能检测到所管理的对象是否已经被释放, 从而避免访问非法内存。
构造新的weak_ptr (公开成员函数) | |
销毁 weak_ptr (公开成员函数) | |
为weak_ptr 赋值 (公开成员函数) |
修改器 | |
释放被管理对象的所有权 (公开成员函数) | |
交换被管理对象 (公开成员函数) | |
观察器 | |
返回管理该对象的 shared_ptr 对象数量 (公开成员函数) | |
检查被引用的对象是否已删除 (公开成员函数) | |
创建管理被引用的对象的shared_ptr (公开成员函数) | |
提供弱指针的基于拥有者顺序 (公开成员函数) |
非成员函数
(C++11) | 特化 算法 (函数模板) |
辅助类
(C++20) | 原子弱指针 (类模板特化) |
weak_ptr 没有重载*和->但可以使用 lock 获得一个可用的 shared_ptr 对象. 注意, weak_ptr 在使用前需要检查合法性.
expired 用于检测所管理的对象是否已经释放, 如果已经释放, 返回 true; 否则返回 false.
lock 用于获取所管理的对象的强引用(shared_ptr). 如果 expired 为 true, 返回一个空的 shared_ptr; 否则返回一个 shared_ptr, 其内部对象指向与 weak_ptr 相同. use_count 返回与 shared_ptr 共享的对象的引用计数. reset 将 weak_ptr 置空. weak_ptr 支持拷贝或赋值, 但不会影响对应的 shared_ptr 内部对象的计数.创建及使用
#include "weak_ptr.h"using namespace std;class CB;class CA;class CA{public: CA(){} ~CA(){ } void Register(const std::shared_ptr& sp) { m_spb = sp; cout << "Register_spib.use_count = " << sp.use_count() << endl; }private: std::weak_ptr m_spb;//这样不会增加m_spb的引用计数 ,那么CA就能释放};class CB{public: CB(){}; ~CB(){ }; void Register(const std::shared_ptr & sp) { m_spa = sp; cout << "Register_spia.use_count = " << sp.use_count() << endl; }private: std::shared_ptr m_spa;//与上条注释的差别所在,无论什么情况下,B的智能指针的释放都得等m_spa的引用计数为1.};std::weak_ptr gw;void f(){ if (auto spt = gw.lock()) { // 使用之前必须复制到 shared_ptr,且会增加原来的shared_ptr的计数 std::cout << *spt << "\n"; cout << "spt.use_count = " << spt.use_count() << endl; } else { std::cout << "gw is expired\n"; }}void weak_ptr_lock(){ { auto sp = std::make_shared (42); cout << "sp.use_count = " << sp.use_count() << endl; gw = sp; f(); cout << "sp.use_count = " << sp.use_count() << endl;//使用计数恢复到1 } f();}void stl_weak_circle(){ std::shared_ptr spa(new CA); std::shared_ptr spb(new CB); std::weak_ptr weak_cb = spb;//并不改变计数 std::weak_ptr weak_cb1(spb);//并不改变计数 cout << "spb.use_count = " << spb.use_count() << endl; cout << "spa.use_count = " << spa.use_count() << endl; spb->Register(spa); spa->Register(spb); cout << "spb.use_count = " << spb.use_count() << endl; cout << "spa.use_count = " << spa.use_count() << endl;}void stl_weak_ptr(){ weak_ptr_lock(); stl_weak_circle();}
转载地址:https://blog.csdn.net/u012516571/article/details/82750999 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!