必须要注意的 C++ 动态内存资源管理(三)——智能指针

释放双眼,带上耳机,听听看~!

七.前言

        在前面一节,我们简单实现了三种类型资源的”指针对象”。其实在c++11的标准库中已经为我们准备了这样的指针对象——智能指针,分别是:shared_ptr , unique_ptr(取代了auto_ptr) , weak_ptr。下面我们简单来介绍一下这三类智能指针的特点和适用情况。

八.shared_ptr智能指针

        shared_ptr指针适用的就是前一节所说的第3类资源指针。         shared_ptr是模板类,所以可以通过任意的指针类型(这里介绍使用的是动态分配的指针,但可以不是动态分配创建的,后面会介绍)来初始化。         shared_ptr对象可以进行拷贝,赋值;当一个对象A拷贝B或者B赋值给A,其根本都是将B的资源共享给A,然后将该资源的引用次数增加;当引用该资源的最后一个shared_ptr对象销毁的时候,会自动把该资源释放掉。下面看shared_ptr的相关操作:

shared_ptr<T>sp(T* res)
将资源res交给sp来管理,资源不使用时会自动释放。
shared_ptr<T>sp = make_spared(params)
用params参数构造T类型的对象并且交给sp来管理。
p
p可以作为条件判断,若p指向一个对象,则为true,否则为false。
p
对shared_ptr<T>对象解引用,得到的是由p管理的T类型对象的引用。
p->mem
等价于(p).mem
p.get()
返回p中保存的T
指针
swap(p,q)
交换p,q各自内部的T
指针,注意p,q类型要相同
p.swap(q)
同上
p = q
p,q必须都是shared_ptr指针,并且各自管理的指针类型能相互转换。此操作会递减p的引用次数,递增q的引用次数;若p的引用次数变为0,则其管理的原内存会自动释放。
shared_ptr<T>sp(q)
使得sp引用q所占资源。
p.reset() , p.reset(q)
放弃p对资源的引用,若为唯一引用,还会释放内存。若传有参数q,则让p引用q所占资源。
p.use_count()
返回与p共享的智能指针数量;可能很慢,主要用于调试。
p.unique()
若p.use_count()为1,返回true,否则返回false

我们用下面这段简单的代码来理解shared_ptr智能指针的使用:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
1int main()
2{
3    {
4        //创建一个动态string初始化为&quot;hello wold&quot;并交付给p来管理。
5        shared_ptr&lt;string&gt; p(new string(&quot;hello world&quot;));
6        {
7            shared_ptr&lt;string&gt; q(p);//用p初始化q,则该字符串增添一个使用者
8            cout &lt;&lt; p.use_count() &lt;&lt; endl;  // &gt;&gt; 2
9            cout &lt;&lt; *q &lt;&lt; endl;   // &gt;&gt; &quot;hello world&quot;
10            *q = *q + &quot;!&quot;;  //解引用修改q管理的string对象。
11        }// 跳出该代码块,q生命周期结束,则该字符串使用者又只剩p一个了。
12        cout &lt;&lt; p.use_count() &lt;&lt; endl;  // &gt;&gt; 1
13        cout &lt;&lt; *p &lt;&lt; endl; &gt;&gt; &quot;hello world!&quot;
14    } // 跳出该代码块,p生命周期结束,则该字符串内存被释放。
15    return 0;  
16}
17

事实上,在不是那种临界资源的情况下,我们管理内存资源大多是使用shared_ptr智能指针。而引用计数实现的有使用者保留,无使用者才释放内存似乎完美地解决了内存释放问题。然而shared_ptr也有缺陷,不过我们留到后面来讲。

九.unique_ptr智能指针

        unique_ptr指针适用的就是前一节所说的第2类资源指针。         unique_ptr是模板类,所以可以通过任意的指针类型(这里介绍使用的是动态分配的指针,但可以不是动态分配创建的,后面会介绍)来初始化。         unique_ptr对象不可以进行拷贝,赋值;只能通过相关函数让unique_ptr让出的支配权然后其他unique_ptr接收。这样就保证了支配权的唯一性。下面看unique_ptr的相关操作:

unique_ptr<T>up(T* res)
将资源res交给up来管理,资源不使用时会自动释放。
p
p可以作为条件判断,若p指向一个对象,则为true,否则为false。
p
对shared_ptr<T>对象解引用,得到的是由p管理的T类型对象的引用。
p->mem
等价于(p).mem。
p.get()
返回p中保存的T
指针。
swap(p,q)
交换p,q各自内部的T
指针,注意p,q类型要相同。
p.swap(q)
同上。
p = q
这是无效操作,unique_ptr不能进行赋值。
unique_ptr<T>sp(q)
这是无效操作,unique_ptr不能进行拷贝。
p = nullptr
释放p指向的对象,并将p置为控。
p.release()
p放弃对指针的控制权,返回资源指针,并将p置为空。
p.reset(T* q)
释放p指向的对象,并将p置为空,若传入参数q,则让p指向q该指针。
p.reset(q.release())
释放掉p指向的对象,q将自身指向对象的控制权转移给p,然后q自身置为空。

我们用下面这段简单的代码来理解unique_ptr智能指针的使用:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
1int main()
2{
3    {
4        //创建一个unique_ptr智能指针q但是目前没有管理任何内存。
5        unique_ptr&lt;string&gt; q;
6        {
7            //创建一个动态string初始化为&quot;hello wold&quot;并交付给p来管理。
8            unique_ptr&lt;string&gt; p(new string(&quot;hello world&quot;));
9            cout &lt;&lt; *p &lt;&lt; endl;   // &gt;&gt; &quot;hello world&quot;
10            q.reset(p.release()); //p将支配权
11
12        }// 跳出该代码块,p生命周期结束,则该字符串使用者又只剩p一个了。
13        cout &lt;&lt; *q &lt;&lt; endl; //&gt;&gt; &quot;hello world&quot;
14    } // 跳出该代码块,q生命周期结束,则该字符串内存被释放。
15    return 0;
16}
17

        特别的,unique_ptr智能指针还可以用于管理动态数组:


1
2
3
4
5
6
7
8
9
10
11
12
13
1int main()
2{
3    //当up释放资源的时候会自动调用 delete[] 销毁其指针。
4    unique_ptr&lt;int[]&gt; up(new int[10]);
5
6    //指向数组的unique_ptr指针重载了[]
7    for(int i = 0 ; i &lt; 10 ; i ++) up[i] = i;
8
9    for(int i = 0 ; i &lt; 10 ; i ++)
10        printf(&quot;%d&quot;,up[i]);       // &gt;&gt; 012345679
11    return 0;
12}
13

        一般来说,我们不常用unique_ptr智能指针来管理动态内存。而是管理类似于数据库连接对象等资源。         有时我们的资源内存不一定是通过动态分配,而且释放有时候也不仅仅是delete 指针而已。所以,我们以后在使用unique_ptr指针的时候还需要传入删除器。这个,我们后面会详细讲到。

十.weak_ptr智能指针

        weak_ptr 是一种不控制所指向对象生命期的智能指针,它指向一个由 shared_ptr 管理的对象。将一个 weak_ptr 绑定到一个 shared_ptr 不会改变 shared_ptr 的引用次数。一旦最后一个指向对象的shared_ptr 被销毁,对象就会被释放;即使这个时候存在 weak_ptr 指向了该对象,对象还是会被释放。所以我们可以把 weak_ptr 看作是一种”弱引用”。

weak_ptr<T>wp
空 weak_ptr 可以指向类型为T的对象
weak_ptr<T>wp(sp)
与shared_ptr sp指向相同的对象,T必须可以转化为sp指向对象的类型。
w = p
p 可以是weak_ptr或者shared_ptr。赋值后w,p共享对象。但是w不干涉对象声明周期。
w.reset()
m置为空。
w.use_count()
与m共享资源的shared_ptr个数。
w.expired()
若w.use_count()为0,返回true,否则返回false。
w.lock()
如果expired为true,返回一个空的shared_ptr;否则返回指向w对象的shared_ptr。

        正是因为weak_ptr是一种”弱引用”,所以,很有可能在使用过程中某个weak_ptr被支配的shared_ptr给销毁了;所以我们在使用weak_ptr之前需要调用w.lock()来判断weak_ptr所指向的内存是否被释放了。

基本上,智能指针的基本操作就讲到这里,下一节会介绍智能指针的删除器以及相关注意事项。

给TA打赏
共{{data.count}}人
人已打赏
安全技术

c++ list, vector, map, set 区别与用法比较

2022-1-11 12:36:11

安全运维

【干货合集】NoSQL技术体系深度解读系列(三):HBase,海量数据存储、超高并发量场景下的NoSQL利器

2021-12-11 11:36:11

个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索