C++智能指针

一. 智能指针

        在 C++ 中,我们申请内存一般使用 new,释放内存时使用 delete,但是有时候我们可能由于疏忽会忘记 delete,或者当程序有多个出口时,也会容易有 new 没有 delete,这样就产生了内存泄漏,如果你的程序是一个需要长期运行的服务器程序,可能运行着几天突然程序就崩溃了,原因也不好定位,所以为了方便内存管理,C++ 引入了智能指针,智能指针的优点在于能够帮助程序员自动释放我们 new 出来的堆内存。

        C++ 标准库有四种智能指针:auto_ptr,unique_ptr,shared_ptr,weak_ptr(auto_ptr 是 C++98 标准的,其余都是 C++11 标准推出的,auto_ptr 现在已经不再使用了),C++11 这三种智能指针都是类模板。

 

二. shared_ptr

(一)概述

        shared_ptr 是一个共享式指针,所有的 shared_ptr 共享对指向内存的所有权,不是被一个 shared_ptr 拥有,而是多个 shared_ptr 之间互相协作。

 

(二)工作原理

        引用计数,use_count 为 0 时就释放对象空间。

 

(三)初始化

       如果是定义了一个智能指针却不初始化,shared_ptr<int> p1,代表定义了一个指向 int 类型对象的智能指针但是目前指向为 empty。推荐使用 make_shared 函数来初始化 shared_ptr,它是标准库的函数模板,安全,高效地分配和使用 shared_ptr。

shared_ptr<int> pint = make_shared<int>(100);

shared_ptr<string> pstr = make_shared<string>(5,'a');

        也可以使用直接初始化的方式 shared_ptr<int> pint(new int(100)) 来创建一个 shared_ptr 并初始化,但是由于 shared_ptr 定义的构造函数是 explicit 的,因此不能使用 shared_ptr<int> pint = new int(100) 来创建一个 shared_ptr 并初始化,因为这种方式隐式要求将一个普通的 int * 转换为 shared_ptr<int>。

 

(四)常用操作

1. use_count

返回多少个智能指针指向该对象,主要用于调试。

2. unique

判断该智能指针是否独占该内存,如果该智能指针不指向任何对象,判断 unique 的时候也是假。

3. reset

不带参数时:放弃指针对对象的掌管权,重置为 nullptr

带参数时:参数一般是一个 new 的空间,相当于放弃指针对当前对象的掌管权,然后将指针指向 new 出来的空间。

4. *

解引用,可以获取指针指向的对象。

5. get

获取指针指针里保存的裸指针,一般用于一些接口需要使用到 C 语言指针的情况。

6. swap

交换两个智能指针所指的对象。

7. =nullptr

该智能指针指向 nullptr,代表解除对该对象的掌握权,引用计数将会减1,如果此时该内存空间的引用计数变为0,会同时释放该内存。

8. 指定删除器以及删除数组问题

智能指针能在一定时机帮我们删除所指向的对象,使用 delete 作为默认的资源析构方式,我们也可以指定自己的删除器取代系统提供的默认删除器,当智能指针需要删除所指向的对象时,编译器就会调用我们提供的删除器。

shared_ptr 指定删除器的方法比较简单,一般只需要在参数中添加具体的删除器函数即可。如果提供了删除器,那么就需要手动删除资源,否则会造成内存泄漏。删除器函数可以是函数,lambda 表达式,重载了 operator() 的类等。

还可以使用 default_delete 来做删除器,default_delete 是标准库里的一个模板类。如:shared_ptr<A> p3(new A[10], default_delete<A[]>()),这样就知道我们使用智能指针指向了一个对象数组,这样就可以正确释放了。

其实,使用 shared_ptr 指向对象数组不需要通过删除器的方式,只需要在定义 shared_ptr 时指为数组类型即可,如:shared_ptr<A[]> p4(new A[10])。

额外说明:就算是两个 shared_ptr 指定了不同的删除器,只要他们指向的对象类型相同,那么这两个 shared_ptr 也是同一个类型,可以放到同一个容器去,vector<shared_ptr<int>> pvec{p1,p2}。

 

三. weak_ptr

(一)概

        weak,英文意思:弱,weak_ptr 是一个弱指针,一般指向一个由 shared_ptr 管理的对象,但是 weak_ptr 不改变引用计数,换言之:如果用一个 shared_ptr 去初始化一个 weak_ptr,不会改变当前内存空间的引用计数。这个 weak_ptr 可以理解为 shared_ptr 的助手,起一种监视作用。

 

(二)初始化

我们创建 weak_ptr 时,一般是用一个 shared_ptr 来初始化,如:shared_ptr<int> sp1(new int(100)); weak_ptr<int> wp1(sp1);

weak_ptr 并不会改变强引用计数,但是弱引用计数会改变强引用计数决定对象的生存期,弱引用计数对对象的生存期没有影响。

weak_ptr 不能直接访问对象,必须使用 expired 函数判断指向的对象是否还存在,如果存在,那么这个 lock 就返回一个指向该对象的 shared_ptr,如果不存在,lock 就返回一个空的 shared_ptr。

 

(三)常用操作

1. use_count

获取与该弱指针共享对象的其他 shared_ptr 的 use_count,即当前所观测对象的强引用计数。

2. expired

判断指向的对象是否过期,即 use_count == 0 是否成立。

3. reset

将弱引用指针设置为空,不影响指向该对象的强引用数量,但是该对象的弱引用数量会减少 1。

4. lock

获取指向该对象的 shared_ptr,注意此时该对象的强引用计数会加 1,当 lock 返回的 shared_ptr 生命期结束时,该对象的强引用计数又会减少 1。

 

(四)shared_ptr和weak_ptr的大小问题

如上图所示,shared_ptr 和 weak_ptr 都包含两部分,一是指向类型对象的指针,二是指向该对象的控制块,所以 shared_ptr 和 weak_ptr 的大小应该是本机器上普通指针大小的两倍。

注意:控制块只会在第一个 shared_ptr 指向对象时被创建出来,以后每次使用一个现有的 shared_ptr 去初始化创建一个新的 shared_ptr 时都不会创建新的控制块,而是新的 shared_ptr 也指向该控制块,引用计数相应增加。

 

四. unique_ptr

(一)概述

unique_ptr 是一个独占式指针,只允许一个 unique_ptr 指针指向 new 出来的对象空间。

 

(二)初始化

常规初始化:unique_ptr<int> up(new int(3));

使用 make_unique 函数初始化:unique_ptr<int> up = make_unique<int>(3);

(注意:make_unique 是 C++14 才出现的,代表更高的性能,如果不需要指定删除器建议使用 make_unique 函数)

 

(三)常用操作

1. unique_ptr不支持的操作

unique_ptr 是独占式指针,因此不支持定义时拷贝构造,也不支持拷贝赋值,但是我们可以将一个快要被销毁的 unique_ptr 拷贝或者赋值给 unique_ptr,最典型的案例就是从函数里返回一个 unique_ptr。

unique_ptr<int> clone(int num)
{
    return unique_ptr<int>(new int(num));
}

2. 移动语义

unique_ptr 虽然不支持拷贝构造,但是支持移动构造和移动赋值,如 unique_ptr<int> up1 = make_unique<int>(3),unique_ptr<int> up2(std::move(up1)),注意移动构造后 up1 为 empty,不可以再使用。

3. release

放弃对资源的掌管权,返回一个裸指针,智能指针被置空。注意返回的裸指针要手动 delete,否则会造成内存泄漏。

4. reset

reset 不带参数时相当于直接释放资源,切断连接,如果带参数,相当于释放原来的资源,让指针指向参数的地址去。

5. =nullptr

相当于释放资源,并将智能指针置为 nullptr。

6. 让智能指针指向数组

unique_ptr<int[]> uptr(new int[10]);

只要在模板参数类型后加上 [],这种用法跟 shared_ptr 类似。

7. get方法

返回智能指针中的裸指针。

8. 解引用

*

9. swap

交换两个指针指向的对象。

10. 使用unique_ptr创建初始化shared_ptr

创建 shared_ptr,如果 unique_ptr 为右值则将它赋值给 shared_ptr,因为 shared_ptr 里有一个显示构造函数,可以接收右值的 unique_ptr,shared_ptr 将接管原来 unique_ptr 的资源。

11. 指定删除器

格式:unique_ptr<指向的对象类型,删除器类型> 智能指针变量名(初始化对象,删除器);

删除器类型:可调用对象,如函数,重载了 operator() 的类等

如上是 5 种可以在定义 unique_ptr 时指定删除器的用法,需要注意的是:删除器是 unique_ptr 的一部分,所以要是删除器类型不同,这两种 unique_ptr 的类型也是不同的,不能放在同一种类型的容器,这点不同于 shared_ptr。

 

(四)unique_ptr 的大小问题

通常情况下,unique_ptr 的大小跟普通指针的大小是一样的,但是如果指定了删除器,unique_ptr 指针的大小可能发生变化。如果删除器是 lambda 指针的话,unique_ptr 就没有变化,否则就会发生变化。

 

五. 智能指针总结

简而言之,智能指针就是简化程序员手动释放内存的操作,避免因为忘记或者没有 delete 产生内存泄漏的问题。 

auto_ptr被废弃的原因

1.auto_ptr 不能在容器中保存。

2.auto_ptr 跟 unique_ptr 类似,都是独占式的指针,但是 unique_ptr 不允许拷贝动作,否则在编译时期就报错了,而 auto_ptr 允许拷贝动作,拷贝完原来的指针就被赋值为空,这样很容易使用户不小心就操作到非法内存。

一开始设计不规范,C++11 已经明确提出不要再使用 auto_ptr。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

椛茶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值