C++的共享指针shared_ptr,自动管理内存

学习视频
跟着视频敲了半天发现的作者的公众号文字版

尽管如此,我仍然觉得还是应该自己总结一下知识点,梳理自己的知识体系

手动管理内存会造成内存泄露,现在C++的智能指针可以在很大程度上帮我们缓解这个问题,降低我们手动管理内存的心智负担。

智能指针类型

  1. shared_ptr
  2. unique_ptr
  3. weak_ptr

共享指针shared_ptr

共享指针会记录有多少个(共享)指针指向同一个物体,当这个数字降为0的时候,程序会自动释放资源,省去我们手动delete的烦恼。
要使用共享指针shared_ptr首先要引入#include<memory>,其次,shared_ptr等名字都是在std命名空间中,所以使用的时候前面要加std::

为了简洁,后面我们假设using namespace std;
在这里插入图片描述

初始化共享指针

方式一:make_shared

定义并初始化一个shared_ptr,make_shared会动态分配一块内存,创建对应的资源,然后让shared_ptr指向他,他是一个模板,可以接受一个类型+对应的初始化参数

#include<memory>
using namespace std;

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

方式二:new

除了make_shared,你也可以用new初始化shared_ptr

#include<memory>
using namespace std;

shared_ptr<int> p {new int(100)};

定义之后,你可以像普通的指针一样使用*、→之类的运算符操作数据,并且多个共享指针可以指向同一个物体,共享控制权。比如

shared_ptr<int> p {new int(100)};
shared_ptr<int> p2 = p;

cout << *p << endl;
*p2 = 321;
cout << *p << endl;

推荐使用make_shared的初始化方式,因为效率更高更安全

自动管理内存

定义一个Ball的类,如下:

class Ball
{
public:
    Ball() { cout << "A ball appears." << endl; }
    ~Ball() { cout << "A ball disappears." << endl; }
    void Bounce() { cout << "A ball jumps." << endl; }
};

定义完毕,当我们创建一个Ball实例的时候,会打印A ball appears. 当我们销毁一个Ball实例的时候,会打印A ball disappears. Ball里面定义了一个公有成员函数Bounce,调用的时候会打印A ball jumps.

下面我们用共享指针来管理Ball的创建与销毁,同时展示一些shared_ptr的基本用法


...

int main()
{
  shared_ptr<Ball> p = make_shared<Ball>;
  cout << p.use_count() << endl;//1
  shared_ptr<Ball> p2 = p;
  cout << p.use_count() << " " << p2.use_count() << endl;//2 2
  shared_ptr<Ball> p3 = p;
  cout << p.use_count() << " " << p2.use_count() << p3.use_count() << endl;//3 3 3
  p.reset();
  p2.reset();
  p3.reset();
}

运行结果:


A ball appears.
1
2 2
3 3 3
A ball disappears.
  1. 创建:不同于裸指针(例如int *),共享指针会记录有多少个共享指针指向同一个物体。
    上面的例子中,创建了3个共享指针指向同一个Ball,然后依次调用use_count()获知有多少shared_ptr指向这个物体
    可以看到创建第一个时打印1,将p2赋值为p时打印两个2,将p3赋值为p时打印三个3。

  2. 销毁:调用3个reset,shared_ptr调用reset之后会重置,不再指向(拥有)原来的物体。三个shared_ptr都重置之后,没有shared_ptr指向开头的Ball,它就自动释放了,所以打印A ball disappears

    对比裸指针和共享指针:
    裸指针:如果其指向的资源是动态分配的内存,且未被手动释放,那么即便指向这块资源的所有裸指针都被摧毁了,这块资源仍然可能未被释放。
    共享指针:共享指针可以在很大程度上帮我们解决这个问题,因为当指向某个物体的共享指针个数降为0的时候,这个物体就会自动销毁。这就是智能指针如何帮助我们管理内存,以避免内存泄漏。

引用计数原理

顾名思义,引用计数的意思就是,数一数有多少个共享指针指向某个物体。

假设有一个物体O,我们创建一个共享指针指向它,这时引用计数变为1,再复制一个这样的共享指针,引用计数变为2。

当某一个共享指针reset或者销毁的时候,引用计数减少1.

如果某个物体的引用计数降为0,程序就会自动释放这个物体。

这样就不需要我们手动delete某个资源,大大降低在复杂环境下人类出错的概率。

获取智能指针内部的裸指针

如果因为特殊的原因,你需要获得指向物体的裸指针,比如某个函数接受的参数是裸指针。那么你可以用get()方法可以获取智能指针内部的裸指针。


Ball* rp = p.get();

注意

即便你仍然有裸指针指向这块内存,当所有的共享指针都被摧毁的时候,底下的资源仍然会被释放,这是需要注意的地方。

因此,良好的习惯是,用共享指针的时候避免跟裸指针混用,不当心可能会访问被释放的资源,这是未定义的行为,可能会造成程序崩溃,还很难修复。
【下一篇进阶用法没看懂,有空再补。。。】

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值