shared_ptr

当指向对象的std::shared_ptr一创建,被管理对象的控制块SharedPtrControlBlock(参考下面的图)就建立了。

被管理的对象的控制块中有引用计数(reference count),当引用计数为0时,被管理的对象会被销毁。

控制块的创建会遵循下面几条规则:

std::make_shared会创建控制块

从 std::unique_ptr 上构造出 std::shared_ptr的时候,会创建控制块

从原始指针上构造 std::shared_ptr的时候会创建控制块

假若我们为一个指针构建了多个控制块,那么会有多个引用计数,意味着对象可能销毁多次,这是非法行为

多次从原始指针上构建智能指针,实际存在多个互不相干的引用计数,大家都为1,然后一旦有一个智能指针不再指向该对象,引用为0就会析构对象,就会出错

定义智能指针的方式

std::shared_ptr<A> p1(new A);
std::shared_ptr<A> p2 = std::make_shared<A>();

在执行std::shared_ptrp1(new A) 的时候,首先会申请数据的内存,然后申请内控制块,因此是两次内存申请

而std::make_shared
()则是只执行一次内存申请,将数据和控制块的申请放到一起

shared_ptr指针的内存结构图,做个参考,可能不完全对

每个shared_ptr都占指针的两倍空间,一个装着原始指针,一个装着计数区域(SharedPtrControlBlock)的指针

用原始指针构造时,会new一个SharedPtrControlBlock出来作为计数存放的地方,然后用指针指向它,计数加减都通过SharedPtrControlBlock指针间接操作

这其中包含两个Count变量,weak count是用于weak_ptr的弱引用计数(weak_ptr要用shared_ptr初始化,使用同一块控制块?)

图片来自

std::enable_shared_from_this 有什么意义? - 孔洽孔洽的回答 - 知乎
https://www.zhihu.com/question/30957800/answer/2700292012

weak_ptr

还记得,shared_ptr内含有指向计数区域(SharedPtrControlBlock)结构体的指针吧

struct SharedPtrControlBlock{
  int shared_count;
  int weak_count;
};

该结构体引进新的int变量weak_count,来作为弱引用计数

每个weak_ptr都占指针的两倍空间,一个装着原始指针,一个装着计数区域的指针(和shared_ptr一样的成员)

大概是这样
class weak_ptr{
  T* ptr;
  SharedPtrControlBlock* count;
};

被管理资源的释放只取决于shared计数,当shared计数为0,才会释放被管理资源,

但是控制块计数区域的释放却取决于shared计数和weak计数,当两者均为0时,才会释放计数区域

这也就是weak_ptr能够使用expired方法判断对象是否被析构的原因,因为该计数区域还在

weak_ptr 一般者是通过 shared _ptr 或另一个weak_ptr 来初始化

weak_ptr不会改变资源的引用计数,只是一个观察者的角色,通过观察shared_ptr来判定资源是否存在,不会影响指向对象(被管理资源)的生命周期,而shared_ptr引用计数为0则会析构对象

weak_ptr持有的引用计数,不是资源的引用计数,而是同一个资源的观察者的计数

weak_ptr没有提供常用的指针操作,无法直接访问资源,需要先通过lock方法提升为shared_ptr强智能指针,才能访问资源

成员函数use_count() 观测资源引用计数

成员函数expired() 功能相当于 use_count()为 0 表示被观测的资源(也就是shared_ptr的管理的资源)被销毁

成员函数lock()从被观测的shared_ptr获得一个可用的shared_ptr对象, 进而操作资源。但当expired()==true的时候,lock()函数将返回一个存储空指针的shared_ptr

来自csdn

原文链接:https://blog.csdn.net/qq_53111905/article/details/122240842

循环引用问题

看下面一段代码

weak_ptr用于解决两个类内部使用shared_ptr互相引用造成的循环,TestA和TestB中weak_ptr如果换成是shared_ptr

那么在离开main函数时,两个在main中创建的shared_ptr失效,本来引用数都是2,各自减一都变为了1,也就是内部的shared_ptr互相指向,成为一个环

两个对象都还被引用着,所以不会析构,造成内存泄露

所以将shared_ptr都改为weak_ptr,离开main时两个在main中创建的shared_ptr都会失效,各自的引用减一,变为0,内部的弱指针不影响引用计数,故而能够正确释放内存

//示例代码1.0 http://www.cnblogs.com/feng-sc/p/5710724.html
#include <memory>
class TestB;
class TestA
{
public:
TestA()
{
std::cout << "TestA()" << std::endl;
}
void ReferTestB(std::shared_ptr<TestB> test_ptr)
{
m_TestB_Ptr = test_ptr;
}
void TestWork()
{
std::cout << "~TestA::TestWork()" << std::endl;
}
~TestA()
{
std::cout << "~TestA()" << std::endl;
}
private:
std::weak_ptr<TestB> m_TestB_Ptr;
}; class TestB
{
public:
TestB()
{
std::cout << "TestB()" << std::endl;
} void ReferTestB(std::shared_ptr<TestA> test_ptr)
{
m_TestA_Ptr = test_ptr;
}
void TestWork()
{
std::cout << "~TestB::TestWork()" << std::endl;
}
~TestB()
{
std::shared_ptr<TestA> tmp = m_TestA_Ptr.lock();
tmp->TestWork();
std::cout << "2 ref a:" << tmp.use_count() << std::endl;
std::cout << "~TestB()" << std::endl;
}
std::weak_ptr<TestA> m_TestA_Ptr;
}; int main()
{
std::shared_ptr<TestA> ptr_a = std::make_shared<TestA>();
std::shared_ptr<TestB> ptr_b = std::make_shared<TestB>();
ptr_a->ReferTestB(ptr_b);
ptr_b->ReferTestB(ptr_a);
std::cout << "1 ref a:" << ptr_a.use_count() << std::endl;
std::cout << "1 ref b:" << ptr_a.use_count() << std::endl;
return 0;
}

shared_from_this要解决的问题

需求: 在类的内部需要自身的shared_ptr 而不是this裸指针,直接从this指针创建?那会导致出现多个引用计数器,会错误析构,因为每调用一次就创建一个指针

场景: 在类中发起一个异步操作, callback回来要保证发起操作的对象仍然有效,std::bind函数和对象,结果对象先被析构了,那还调用个屁

异步回调的时候对象可能已经被销毁了 所以使用shared_ptr 传出去就是保证最少还有一个引用计数维持对象的生命周期直到回调结束

struct A {
void func() {
std::shared_ptr<A> local_sp_a(this);
// do something with local_sp_a
}
};

上面这段代码,由成员函数直接在this上构建智能指针,当离开该函数作用域,local_sp_a就会失效,则引用计数少1,则会立即析构this

改成这样

struct A : public enable_shared_from_this {
void func() {
std::shared_ptr<A> local_sp_a = shared_from_this();
// do something with local_sp
}
};

shared_from_this()会查找当前对象控制块,然后创建一个新的std::shared_ptr关联这个控制块。

用这个函数之前,是假设当前对象已经存在一个关联的控制块。因此,必须已经存在一个指向当前对象的std::shared_ptr。

如果没有std::shared_ptr指向当前对象(即当前对象没有关联控制块),那么shared_from_this会抛出一个异常。

智能指针出现的意义

智能指针的应用场景其实大概就了解了,

有时,程序不能正常释放内存资源,忘记释放就不说了,如出现异常,后面的代码不会执行,可能析构对象的代码就被忽略了

有时候,对象已经不存在了,被析构了,然而程序仍在调用该对象的成员函数,则会出错,可以通过weak_ptr判断对象是否还在

异步,以及多线程的情况下,都会出现对象可能不存在了的情况

可以通过weak_ptr的lock方法判断是否为nullptr来判断对象是否被析构

智能指针使用总结

当你需要一个独占资源所有权(访问权+生命控制权)的指针,且不允许任何外界访问,使用std::unique_ptr

当你需要一个共享资源所有权(访问权+生命控制权)的指针,使用std::shared_ptr

当你需要一个能访问资源,但不控制其生命周期的指针,使用std::weak_ptr

一个shared_ptr和n个weak_ptr搭配使用而不是n个shared_ptr

因为一般模型中,最好总是被一个指针控制生命周期,然后可以被n个指针控制访问

参考链接:https://www.cnblogs.com/KillerAery/p/9096558.html

智能指针 shared_ptr weak_ptr shared_from_this 笔记的更多相关文章

  1. 深入学习c++--智能指针(二) weak_ptr(打破shared_ptr循环引用)

    1. 几种智能指针 1. auto_ptr: c++11中推荐不使用他(放弃) 2. shared_ptr: 拥有共享对象所有权语义的智能指针 3. unique_ptr: 拥有独有对象所有权语义的智 ...

  2. [6] 智能指针boost::weak_ptr

    [1]boost::weak_ptr简介 boost::weak_ptr属于boost库,定义在namespace boost中,包含头文件 #include<boost/weak_ptr.hp ...

  3. c/c++ 智能指针 shared_ptr 使用

    智能指针 shared_ptr 使用 上一篇智能指针是啥玩意,介绍了什么是智能指针. 这一篇简单说说如何使用智能指针. 一,智能指针分3类:今天只唠唠shared_ptr shared_ptr uni ...

  4. C++智能指针shared_ptr

    shared_ptr 这里有一个你在标准库中找不到的—引用数智能指针.大部分人都应当有过使用智能指针的经历,并且已经有很多关于引用数的文章.最重要的一个细节是引用数是如何被执行的—插入,意思是说你将引 ...

  5. STL源码剖析-智能指针shared_ptr源码

    目录一. 引言二. 代码实现 2.1 模拟实现shared_ptr2.2 测试用例三. 潜在问题分析 你可能还需要了解模拟实现C++标准库中的auto_ptr一. 引言与auto_ptr大同小异,sh ...

  6. c/c++ 智能指针 shared_ptr 和 new结合使用

    智能指针 shared_ptr 和 new结合使用 用make_shared函数初始化shared_ptr是最推荐的,但有的时候还是需要用new关键字来初始化shared_ptr. 一,先来个表格,唠 ...

  7. 智能指针shared_ptr新特性shared_from_this及weak_ptr

    enable_shared_from_this是一个模板类,定义于头文件<memory>,其原型为: template< class T > class enable_shar ...

  8. 智能指针shared_ptr的用法

    为了解决C++内存泄漏的问题,C++11引入了智能指针(Smart Pointer). 智能指针的原理是,接受一个申请好的内存地址,构造一个保存在栈上的智能指针对象,当程序退出栈的作用域范围后,由于栈 ...

  9. 智能指针之 weak_ptr

    1. weak_ptr 介绍 std::weak_ptr 是一种智能指针,它对被 std::shared_ptr 管理的对象存在非拥有性("弱")引用.在访问所引用的对象指针前必须 ...

  10. 智能指针 shared_ptr 解析

    近期正在进行<Effective C++>的第二遍阅读,书里面多个条款涉及到了shared_ptr智能指针,介绍的太分散,学习起来麻烦.写篇blog整理一下. LinJM   @HQU s ...

随机推荐

  1. 使用 SSH 连接 Git 服务器

    关于 SSH SSH (Secure Shell) 是一种安全的远程登录协议,可以让你通过安全的加密连接进行远程登录.目前,Mac.Windows 10.Linux 系统均有内置 OpenSSH 客户 ...

  2. Qt网络编程-书接上文,浅谈TCP文件收发,以及心跳包

    qt网络编程-书接上文,浅谈文件收发 上文Qt网络编程-从0到多线程编程中谈到 在qt中的qtcpsocket通讯的用法,接下来浅谈一下关于tcp通讯的实际应用,当然了由于是浅谈,也不能保证其功能的完 ...

  3. java定时任务Quartz整理

    目录 一.Quartz介绍 二.Quartz的组成 三.使用java实现一个简单的Quartz例子 四.使用Springboot整合Quartz定时任务框架 五.使用Springboot+mybati ...

  4. [常用工具] PyAutoGUI使用教程

    PyAutoGUI使用教程 目录 PyAutoGUI使用教程 1 基础知识 2 一般函数 3 故障保险 4 鼠标函数 4.1 鼠标移动 4.2 鼠标拖动 4.3 鼠标单击 4.4 鼠标滚动 4.5 鼠 ...

  5. P1605迷宫——题解

    展开 题目背景 给定一个N*M方格的迷宫,迷宫里有T处障碍,障碍处不可通过.给定起点坐标和终点坐标,问: 每个方格最多经过1次,有多少种从起点坐标到终点坐标的方案.在迷宫中移动有上下左右四种方式,每次 ...

  6. 轻松解决 CSS 代码都在一行的问题

    前言 最近在做博客园的界面美化,用的是博客园[guangzan]的开源项目,配置超级简单,只需要复制粘贴代码就好啦. 但在粘贴 CSS 代码时遇到一个问题,那就是所有代码都挤在了一行,没有一点排板的样 ...

  7. 结构型模式 - 代理模式Proxy

    学习而来,代码是自己敲的.也有些自己的理解在里边,有问题希望大家指出. 代理模式的定义与特点         代理模式的定义:由于某些原因需要给某对象提供一个代理以控制对该对象的访问.这时,访问对象不 ...

  8. 朋友圈那串神秘字符背后的开源项目「GitHub 热点速览」

    ​如果你这周没刷到类似 "npub1sg6plzptd64u62a878hep2kev88swjh3tw00gjsfl8f237..." 的一串字符,那就说明本期 GitHub T ...

  9. Zabbix“专家坐诊”第180期问答汇总

    问题一 Q:老师,请教个问题,zabbix通过自动发现扫描网段,然后添加主机,有没有什么办法区分路由器或者交换机类型的方法,这样才能把交换机模板或者路由器模板挂给对应的主机A:不多的话, 批量加2次模 ...

  10. 【.NET 8】ASP.NET Core计划 - 支持更完善的AOT发布

    .NET7.0刚发布不久,.NET社区开始了.NET8.0的开发,重心重新回到了新功能的迭代. 我们知道在.NET7.0中一个令人激动的特新就是支持了NativeAOT,我们可以通过NativeAOT ...