C++11中的shared_ptr智能指针入门级介绍

“指针问题”

C语言中的指针语法是很多初学者的噩梦,但是由于指针能够便捷的管理内存,后进者C++并没有抛弃指针语法。不过,由于指针过于灵活,很容易为程序带来内存溢出、内存泄漏等问题,即使是经验老道的程序员也不敢说能够完全避免这些问题,所以C++提供了引用语法,以期解决指针带来的问题。

可惜的是,近些年的实践证明C++是离不开指针(这一点我们以后再谈)的,因此早期C语言程序员使用指针面临的问题,C++程序员也不得不考虑。C++的语法比C语言的语法复杂得多,若是不能提供一种缓解“指针问题”的方案,那真的有些说不过去了,所以“智能指针”就被设计出来了。

本文将讨论C++11中的std::shared_ptr智能指针。

C++中的指针的一个典型用法就是管理一段内存。常规做法是通过类似于 malloc() 的内存管理函数,或者new关键字等方法分配一段内存,并且定义一个指针指向这块内存,之后便可通过指针访问这块内存。

不过一般来说,malloc() 或者new关键字分配的内存不会被系统自动回收,这意味着即使分配的内存不再被使用,但若是程序员不主动释放分配的内存,这块内存将永远不能再被别的逻辑使用,这就是所谓的“内存泄漏”。

可能有读者会想,保证 new/malloc() 和 delete/free() 的配对使用不就好了吗?这有什么难的!的确,在简单的项目里比较容易保证C++程序不会发生“内存泄漏”,但若是项目稍稍复杂一些,可能要使用别人提供的接口函数,这时再去确定内存的生命周期就稍显麻烦了,特别是有的同事懒得写文档,而他的接口函数源代码不可得的情况下。

简而言之,C++中类C语言的普通指针管理内存的确有着不方便的地方——程序员必须非常清楚整个架构,才能知道某段已分配的内存是否仍在被使用中,进而确保安全的释放已经不再使用的内存,这并非易事。

std::shared_ptr<> 是什么?

std::shared_ptr<>是C++11标准中的智能指针类,它很聪明,能够知道自己管理的对象是否还有人使用,若是没有人再使用自己管理的对象,就会自动的删除该对象。所以,shared_ptr能够在最大程度上帮助C++程序员避免内存泄漏问题,也能够避免“悬空指针”的出现。

正如shared_ptr的字面含义,“shared”意味着共享,即不同的shared_ptr可以与同一个指针建立联系,其内部通过“引用计数机制”实现自动管理指针。

通常来说,每一个shared_ptr对象在其内部都管理两部分内容:

  1. shared_ptr 对象本身
  2. shared_ptr 对象管理的内容

“引用计数机制”

假设计划使用指针 p 指向一块分配的内存,现在使用C++的智能指针类 shared_ptr 自动管理这块内存。

  • 当 shared_ptr 类实例化一个对象与指针 p 绑定时,其内部的构造函数会将对应指针 p 的计数加 1。
  • 当 shared_ptr 对象完成自己的生命周期,它的析构函数会将指针 p 对应的计数减 1。

引用计数减少到 0,就意味着没有 shared_ptr 对象还与指针 p 管理的内存绑定,也即没有人再使用这块内存了,于是 shared_ptr 的析构函数调用“delete”方法释放这块内存。

创建一个shared_ptr对象

将一个裸指针绑定到 shared_ptr 对象上是简单的,例如:

std::shared_ptr<int> p1(new int());

上面这行C++代码在堆上分配了两块内存,一块用于存储new出来的 int 值,一块用于存储 shared_ptr 对象本身。正如前面讨论的,shared_ptr 对象在其内部使用“引用计数”机制管理“new int()”,这里“计数”的初始值显然是 1,因为暂时只有一个 shared_ptr 对象指向“new int()”上。

C++智能指针类 shared_ptr 提供了成员函数 use_count() 用于检查实际的“计数值”,请参考稍后的实例。

怎样将指针“赋值”给shared_ptr?

因为 shared_ptr 类的构造函数是 Explicit 的,所以像下面这样的C++代码是非法的:

// 非法 std::shared_ptr<int> p1 = new int();

不过,除了前文提到的那样通过shared_ptr构造函数绑定指针,还有一种推荐的方法用于绑定指针:

std::shared_ptr<int> p1 = std::make_shared<int>();

std::make_shared执行了类似于 shared_ptr 构造函数类似的工作:在堆上分配两块内存,一块用于存储 int 值,一块用于存储 shared_ptr 对象本身。

“解绑”

现在我们知道了怎样将普通的裸指针与 shared_ptr 对象绑定,那么怎样才能“解绑”呢?使用 shared_ptr 类提供的 reset() 成员函数即可:

p1.reset();

reset() 函数会将绑定指针的计数减 1,如果计数减小到 0,那么它将删除绑定的指针。reset() 函数也可以接收一个参数,例如:

p1.reset(new int(32));

这种情况下,它将与一个新指针“new int(32)”绑定,因此内部的计数将重新变为 1。

如果想直接解绑 p1 绑定的指针,还可以使用 nullptr,例如:

p1 = nullptr;

shared_ptr 并不是严格意义的“指针”

C++中的 shared_ptr 对象在某种程度上虽然表现的很像传统C语言中的指针,例如可以使用 *,->运算符,但是它并不是严格意义上的“指针”,这一点应该始终明白。

#C/C++#
全部评论
指针很重要啊,代码都离不开指针啊
点赞 回复 分享
发布于 2022-08-14 13:46

相关推荐

点赞 评论 收藏
分享
1 3 评论
分享
牛客网
牛客企业服务