C++Smart|C++Smart Pointer 智能指针详解
目录
- 一、为啥使用智能指针呢
- 二、shared_ptr智能指针
- 三、unique_ptr智能指针
- 四、weak_ptr智能指针
- 五、智能指针怎么解决交叉引用,造成的内存泄漏
- 5.1交叉引用的栗子:
- 5.2解决方案
- 六、智能指针的注意事项
- 总结
一、为啥使用智能指针呢
标准库中的智能指针:std::auto_ptr--single ownership(C++98中出现,缺陷较多,被摒弃)std::unique_ptr--single ownership(C++11替代std::auto_ptr,用于单线程)std::shared_ptr--shared ownership(C++11,用于多线程)std::weak_ptr--temp/no ownership (C++11)Introduced in C++ 11Defined inheader.
首先看一个下面的栗子,左边是木有使用智能指针的情况,当执行
foo()
函数,其中的e
指针会在bar(e)
时传入bar
函数,但是在bar
函数结束后没有人为delete e
时,就会导致内存泄漏;但是在右边的栗子中,使用了unique_ptr
智能指针(single ownership),就能防止内存泄漏。文章图片
智能指针主要用于管理在堆上分配的内存,它将普通的指针封装为一个栈对象。当栈对象的生存周期结束后,会在析构函数中释放掉申请的内存,从而防止内存泄漏。
auto_ptr
智能指针:(C++11出来前只有这种智能指针)当对象拷贝或者赋值后,前面的对象就悬空了。unique_ptr
智能指针:防止智能指针拷贝和复制。shared_ptr
智能指针:通过引用计数的方式来实现多个shared_ptr
对象之间共享资源。weak_ptr
智能指针:可以从一个shared_ptr
或另一个weak_ptr
对象构造,它的构造和析构不会引起引用记数的增加或减少。
- 智能指针分为两类:
- 一种是可以使用多个智能指针管理同一块内存区域,每增加一个智能指针,就会增加1次引用计数,
- 另一类是不能使用多个智能指针管理同一块内存区域,通俗来说,当智能指针2来管理这一块内存时,原先管理这一块内存的智能指针1只能释放对这一块指针的所有权(ownership)。
- 按照这个分类标准,
auto_ptr
unique_ptr
weak_ptr
属于后者,shared_ptr
属于前者。
shared_ptr
进行初始化时不能将一个普通指针直接赋值给智能指针,因为一个是指针,一个是类。可以通过make_shared
函数或者通过构造函数传入普通指针。并可以通过get
函数获得普通指针。#include #includeusing namespace std; class report{private:string str; public:report(const string s):str(s) //构造方法{cout<<"1 report Objecthas been build!"< talk(); }{shared_ptr ptr(new report(talk)); ptr->talk(); }{unique_ptr ptr(new report(talk)); ptr->talk(); }return 0; }
文章图片
二、shared_ptr智能指针
shared_ptr
实现了共享拥有的概念,利用“引用计数”来控制堆上对象的生命周期。share_ptr
的生命周期:文章图片
原理:在初始化的时候引用计数设为1,每当被拷贝或者赋值的时候引用计数+1,析构的时候引用计数-1,直到引用计数被减到0,那么就可以delete掉对象的指针了。他的构造方式主要有以下三种:
shared_ptr
- 第一种空构造,没有指定
shared_ptr
管理的堆上对象的指针,所以引用计数为0,后期可以通过reset()
成员函数来指定其管理的堆上对象的指针,reset()
之后引用计数设为1。 - 第二种是比较常见的构造方式,构造函数里面可以放堆上对象的指针,也可以放其他的智能指针(如
weak_ptr
)。 - 第三种构造方式指定了
shared_ptr
在析构自己所保存的堆上对象的指针时(即引用计数为0时)所要调用的函数,这说明我们可以自定义特定对象的特定析构方式。同样的,reset()
成员函数也可以指定析构时调用的指定函数。 - 第四种方法:较常见,构造
shared_ptr
的方式(最安全):
auto ptr = make_shared(args);
上面第四种方法,使用标准库里边的make_shared<>()
模板函数。该函数会调用模板类的构造方法,实例化一个堆上对象,然后将保存了该对象指针的shared_ptr
返回。参数是该类构造函数的参数,所以使用make_shared<>()
就好像单纯地在构造该类对象一样。auto
是C++11的一个关键字,可以在编译期间自动推算变量的类型,在这里就是shared_ptr
类型。
文章图片
shared_ptr
的其他成员函数:use_count() //返回引用计数的个数unique() //返回是否是独占所有权(use_count是否为1)swap()//交换两个shared_ptr对象(即交换所拥有的对象,引用计数也随之交换)reset()//放弃内部对象的所有权或拥有对象的变更, 会引起原有对象的引用计数的减少
三、unique_ptr智能指针 注意
unique_ptr
是single ownership的,不能拷贝。其构造方式如下:文章图片
【C++Smart|C++Smart Pointer 智能指针详解】
unique_ptr
的生命周期:文章图片
四、weak_ptr智能指针
文章图片
五、智能指针怎么解决交叉引用,造成的内存泄漏 结论:创建对象时使用
shared_ptr
强智能指针指向,其余情况都使用weak_ptr
弱智能指针指向。5.1 交叉引用的栗子:
当A类中有一个指向B类的
shared_ptr
强类型智能指针,B类中也有一个指向A类的shared_ptr
强类型智能指针。main
函数执行后有两个强智能指针指向了对象A,对象A的引用计数为2,B类也是:#include#include using namespace std; class B; class A{public:shared_ptr _bptr; }; class B{public:shared_ptr _aptr; }; int main(){shared_ptr aptr(new A()); shared_ptr bptr(new B()); aptr->_bptr = bptr; bptr->_aptr = aptr; return 0; }
文章图片
而当主函数
main
的return
返回后,对象A
的引用计数减一变为1(aptr
没指向A
对象了),B
对象也是,引用计数不为0,即不能析构2个对象释放内存,造成内存泄漏。5.2 解决方案
将类A和类B中的
shared_ptr
强智能指针都换成weak_ptr
弱智能指针;class A{public:weak_ptr _bptr; }; class B{public:weak_ptr _aptr; };
weak_ptr
弱智能指针,虽然有引用计数,但实际上它并不增加计数,而是只观察对象的引用计数。所以此时对象A的引用计数只为1,对象B的引用计数也只为1。文章图片
六、智能指针的注意事项
- 避免同一块内存绑定到多个独立创建的shared_ptr上,因此要不使用相同的内置指针初始化(或reset)多个智能指针,不要混合使用智能指针和普通指针,坚持只用智能指针。
- 不delete get() 函数返回的指针,因为这样操作后,
shared_ptr
并不知道它管理的内存被释放了,会造成shared_ptr重复析构。 - 不使用 get()函数初始化或(reset)另外的智能指针。
shared_ptrp = make_share (42); int *q = p.get(); {shared_ptr (q); } // 程序块结束,q被销毁,指向的内存被释放。int foo = *p; //出错,p指向的内存已经被q释放,这是用get() 初始化另外的智能指针惹得祸。// 请记住,永远不要用get初始化另外一个智能指针。
能使用
unique_ptr
时就不要使用share_ptr
指针(后者需要保证线程安全,所以在赋值or销毁时overhead开销更高)。总结 本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注脚本之家的更多内容!
推荐阅读
- 人工智能|人工智能---深度学习从感知机到神经网络
- 脑技术|人工智能、机器学习、神经网络和深度学习的发展历程(下)
- 超分辨率技术AI人工智能老照片修复自动人像脑补照片高清重建人脸模糊图片变清晰软件
- 人工智能|隐马尔可夫模型基础介绍
- 嵌入式|六轴陀螺仪简介及在智能车中的应用
- 人工智能|硕士小哥将iphoneX充电口改成Type-C,成品在eBay上拍卖,出价已超过56万元
- 人工智能+大数据|特征工程(特征预处理(无量纲化处理))
- 人工智能+大数据|特征工程(特征提取入门学习(附案例))
- 微软智能云Azure在华新增数据中心区域正式启用
- 人工智能|重磅!李沐在斯坦福开新课了!