c++ 智能指针 shared_ptr

news2024/11/17 23:55:25

C++ 智能指针 shared_ptr 详解与示例_码农小明的博客-CSDN博客_shared_ptr

一、简介

        shared_ptr 是c++11的智能类,可以在任何地方都不使用的时候自动删除和相关指针,从而彻底消除内存泄漏和指针悬空的问题。

  她遵循共享所有权,即不同的shared_ptr 对象可以与相同的指针想关联

每个shared_ptr对象在内部纸箱两个内存位置:

1、指向对象的指针

2、用于控制引用计数数据的指针

二、shared_ptr 对象的基本操作

1、创建

    std::shared_ptr<int> p1(new int());  // 有两个内存,1是存放int 的地址,2 是用来引用计数的内存地址(在哪里引用了) 
    p1.use_count();  // 这个指针别引用了多长次

 2、创建空的shared_ptr对象

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

因为带有参数的 shared_ptr 构造函数是 explicit 类型的,所以不能像这样std::shared_ptr<int> p1 = new int();隐式调用它构造函数。创建新的shared_ptr对象的最佳方法是使用std :: make_shared

这里可以说一下explicit  的所用:

explicit关键字用来修饰类的构造函数,被修饰的构造函数的类,不能发生相应的隐式类型转换,只能以显示的方式进行类型转换。

3、分离关联的原始指针

使用reset() 函数,但是这里有两种方式reset()和reset(params);

不带参数的reset()

p1.reset();  // 引用的计数会减一,如果计数为0 ,那么这个指针就会删除

带参数的reset():

p1.reset(new int(34)); // 将内部指向新的指针,因此其作用计算将变成1 

使用nullptr重置:

p1 = nullptr; 
if  1

int main()
{

  //  std::shared_ptr<int> p1(new int());  // 有两个内存,1是存放int 的地址,2 是用来引用计数的内存地址(在哪里引用了) 
    //p1.use_count();  // 这个指针别引用了多长次


    // 创建一个空对象
    std::shared_ptr<int> p1 = std::make_shared<int>();
    std::cout << "p1  空 的引用次数  " << p1.use_count() << std::endl;
    *p1 = 3334;
    std::cout << "  p1=  " <<*p1 <<std::endl;  //输出3334
    std::cout << "  p1的内存地址  " << p1 << std::endl;  //

    // 打印引用个数:1
    std::cout << "p1  的引用次数  " << p1.use_count() << std::endl;
    // 创建第二个对象p2 使其指向同一个指针
    std::shared_ptr<int> p2(p1);
    std::cout << "  p2的内存地址  " << p2 << std::endl;  //
    // 下面两个输出都是:2
    std::cout << "p2 引用次数  = " << p2.use_count() << std::endl;
    std::cout << "p1 引用次数  = " << p1.use_count() << std::endl;


    // 比较智能指针,p1 等于 p2  ,由于p1  p2 的地址是一样的,所以p1==p2
    if (p1 == p2)
    {
        std::cout << "p1 and p2 are pointing to same pointer\n";
    }
    std::cout << "Reset p1 " << std::endl;

    // 无参数调用reset,无关联指针,引用个数为0
    p1.reset();
    std::cout << "p1 Reference Count = " << p1.use_count() << std::endl;

    // 带参数调用reset,引用个数为1
    p1.reset(new int(11));
    std::cout << "p1  Reference Count = " << p1.use_count() << std::endl;

    // 把对象重置为NULL,引用计数为0
    p1 = nullptr;
    std::cout << "p1  Reference Count = " << p1.use_count() << std::endl;
    if (!p1) {
        std::cout << "p1 is NULL" << std::endl; // 输出
    }


    return  0;

}
# endif

 4、自定义删除器 Deleter

delete Pointer;

当 shared_ptr 对象指向数组

// 需要添加自定义删除器的使用方式
std::shared_ptr<int> p3(new int[12]);   // 仅用于演示自定义删除器

// 指向数组的智能指针可以使用这种形式
std::shared_ptr<int[]> p3(new int[12]);  // 正确使用方式

给shared_ptr添加自定义删除器

#if  1   // share_ptr 指针的使用

struct A
{
    A()
    {
        std::cout << " A \n";
    }
    ~A()
    {
        std::cout << " ~A \n";
    }
};

void deleter(A* a)
{
    std::cout << "A   Deleter\n";
    delete[] a;
}


int main()
{
    std::shared_ptr<A>p4(new A[3], deleter);


    std::cout << "--------------------------------------------------------\n";

  //  std::shared_ptr<int> p1(new int());  // 有两个内存,1是存放int 的地址,2 是用来引用计数的内存地址(在哪里引用了) 
    //p1.use_count();  // 这个指针别引用了多长次


    // 创建一个空对象
    std::shared_ptr<int> p1 = std::make_shared<int>();
    std::cout << "p1  空 的引用次数  " << p1.use_count() << std::endl;
    *p1 = 3334;
    std::cout << "  p1=  " <<*p1 <<std::endl;  //输出3334
    std::cout << "  p1的内存地址  " << p1 << std::endl;  //

    // 打印引用个数:1
    std::cout << "p1  的引用次数  " << p1.use_count() << std::endl;
    // 创建第二个对象p2 使其指向同一个指针
    std::shared_ptr<int> p2(p1);
    std::cout << "  p2的内存地址  " << p2 << std::endl;  //
    // 下面两个输出都是:2
    std::cout << "p2 引用次数  = " << p2.use_count() << std::endl;
    std::cout << "p1 引用次数  = " << p1.use_count() << std::endl;


    // 比较智能指针,p1 等于 p2  ,由于p1  p2 的地址是一样的,所以p1==p2
    if (p1 == p2)
    {
        std::cout << "p1 and p2 are pointing to same pointer\n";
    }
    std::cout << "Reset p1 " << std::endl;

    // 无参数调用reset,无关联指针,引用个数为0
    p1.reset();
    std::cout << "p1 Reference Count = " << p1.use_count() << std::endl;

    // 带参数调用reset,引用个数为1
    p1.reset(new int(11));
    std::cout << "p1  Reference Count = " << p1.use_count() << std::endl;

    // 把对象重置为NULL,引用计数为0
    p1 = nullptr;
    std::cout << "p1  Reference Count = " << p1.use_count() << std::endl;
    if (!p1) {
        std::cout << "p1 is NULL" << std::endl; // 输出
    }


    return  0;

}
# endif

 三、shared_ptr 相对于普通指针的优缺点

缺少 ++, – – 和 [] 运算符

与普通指针相比,shared_ptr仅提供-> 、*==运算符,没有+-++--[]等运算符

NULL检测

当我们创建 shared_ptr 对象而不分配任何值时,它就是空的;普通指针不分配空间的时候相当于一个野指针,指向垃圾空间,且无法判断指向的是否是有用数据。

 

创建 shared_ptr 时注意事项


不要使用同一个原始指针构造 shared_ptr
创建多个 shared_ptr 的正常方法是使用一个已存在的shared_ptr 进行创建,而不是使用同一个原始指针进行创建。
示例:

    int *num = new int(23);
    std::shared_ptr<int> p1(num);
    
    std::shared_ptr<int> p2(p1);  // 正确使用方法
    std::shared_ptr<int> p3(num); // 不推荐

    std::cout << "p1 Reference = " << p1.use_count() << std::endl; // 输出 2
    std::cout << "p2 Reference = " << p2.use_count() << std::endl; // 输出 2
    std::cout << "p3 Reference = " << p3.use_count() << std::endl; // 输出 1


假如使用原始指针num创建了p1,又同样方法创建了p3,当p1超出作用域时会调用delete释放num内存,此时num成了悬空指针,当p3超出作用域再次delete的时候就可能会出错。

不要用栈中的指针构造 shared_ptr 对象
shared_ptr 默认的构造函数中使用的是delete来删除关联的指针,所以构造的时候也必须使用new出来的堆空间的指针。
示例:

int main()
{
   int x = 12;
   std::shared_ptr<int> ptr(&x);
   return 0;
}


当 shared_ptr 对象超出作用域调用析构函数delete 指针&x时会出错。

建议使用 make_shared


为了避免以上两种情形,建议使用make_shared()<>创建 shared_ptr 对象,而不是使用默认构造函数创建。

std::shared_ptr<int> ptr_1 = make_shared<int>();
std::shared_ptr<int> ptr_2 (ptr_1);


另外不建议使用get()函数获取 shared_ptr 关联的原始指针,因为如果在 shared_ptr 析构之前手动调用了delete函数,同样会导致类似的错误。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/30025.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

CVPR‘15 Joint action recognition and pose estimation from video

任务&#xff1a;action recognition and pose estimation 思路&#xff1a;对动作和姿态进行统一建模&#xff0c;将动作分成姿态&#xff0c;再将姿态分成part&#xff0c;学习三种level特征&#xff0c;通过动态规划有效的推断动作标签和姿态。 方法&#xff1a;统一建模…

通俗理解计算机操作系统的作用

“操作系统”&#xff0c;简称“OS”&#xff0c;是一个包含多个部分和多个目标的大型程序。 它的第一项工作是在你第一次打开计算机时启动并运行计算机。它的另一项工作是启动和结束应用程序&#xff0c;并给每个程序一个运行时间。它是那台计算机上所有其他程序的“老大”。当…

【App自动化测试】(一)Appium和移动端自动化

目录1. 目前mobile自动化解决方案1.1 iOS和Android 测试工具1.2 自动化工具的选择1.3 选择自动化工具的考虑因素2. Appium介绍2.1 Appium介绍2.2 多架构支持2.3 推荐Appium的理由2.4. Appium框架介绍2.4.1 Appium引擎列表2.4.2 Appium设计理念前言&#xff1a; 本文为在霍格沃兹…

多智能体强化学习MARL的概念和框架

1.多智能体强化学习 系统里的agents数量大于1&#xff0c;agents彼此之间不是独立的 每个agent的动作都能影响到下一个状态每个agent都能影响到其他agent 除非agent之间是独立的&#xff0c;否则单一agent的RL方法不适合MARL 2.MARL的类型 Fully cooperative&#xff08;完…

智慧医院智慧医疗解决方案

IBM于2009年提出“智慧医疗”这一理念。作为“智慧的地球”战略的重要组成部分&#xff0c;致力于构建一个“以病人为中心”的医疗服务体系。通过在服务成本、服务质量和服务可及性三方面取得一个良好的平衡&#xff0c;从而优化医疗实践成果、创新医疗服务模式和业务市场&…

Unity插件Obi.Rope详解

前言 Obi.Rope插件的使用方法 绳子的创建 创建后的Obi Rope&#xff0c;Inspector面板如下所示&#xff0c;组件比较多&#xff0c;其中Obi Solver是绳子&#xff0c;布料&#xff0c;液体等的总处理器&#xff0c;也可以单独分出来此组件为一个独立的游戏对象。 绳子的形状…

风控标签体系的使用与介绍

随着大数据技术的深入研究与应用&#xff0c;企业的专注点日益聚焦于怎样利用大数据来为精细化运营及精准营销服务&#xff0c;进而深入挖掘潜在的商业价值。于是&#xff0c;用户画像的概念也就应运而生。 在推荐系统中&#xff0c; 【推荐系统内容&#xff0c;详情可以关注番…

弘玑Cyclone2022产品发布会:全新上线智能文档处理交互平台——尚书台

近日&#xff0c;在弘玑Cyclone“智无边界&#xff0c;数字未来”发布会上&#xff0c;弘玑Cyclone2022年超级自动化系列产品全新亮相&#xff0c;首席产品官贾岿博士带领产品团队以创新技术对新时代语境下的数字生产力进行了全新解读。 上期为大家介绍了人人可用的数字化工作…

Java工具库Guava的数学运算常用方法示例代码

场景 Java核心工具库Guava介绍以及Optional和Preconditions使用进行非空和数据校验&#xff1a; Java核心工具库Guava介绍以及Optional和Preconditions使用进行非空和数据校验_霸道流氓气质的博客-CSDN博客_guava 校验 为什么使用 Guava Math 1、Guava Math 针对各种不常见…

web网页设计期末课程大作业:美食餐饮文化主题网站设计——中华美德6页面HTML+CSS+JavaScript

&#x1f380; 精彩专栏推荐&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业…

MCE | 表观遗传:YTHDF蛋白调节 m6A-RNA

近期&#xff0c;美国康奈尔大学 Samie R. Jaffrey 研究组在 Cell 上发表了题为 “A Unified Model for the Function of YTHDF Proteins in Regulating m6A-Modified mRNA” 的研究&#xff0c;揭示了 YTHDF 蛋白调节 m6A 修饰的 mRNA 的功能统一模型。与“不同的 m6A 位点结合…

反向传播——机器学习

目录 一、实验内容 二、实验过程 1、算法思想 2、算法原理 3、算法分析 三、源程序代码 四、运行结果及分析 五、实验总结 一、实验内容 掌握线性反向传播的原理&#xff1b;掌握线性反向传播的算法Python实现&#xff1b;熟悉非线性反向传播的原理&#xff1b;掌握非线性…

图的存储方式

一、邻接矩阵 图的邻接矩阵存储方式就是用两个数组来表示图。一个一维数组存储图的顶点信息&#xff0c;另一个二维数组存储图中边的信息。 对于无向图来说&#xff0c;我们可以用1表示两顶点相连&#xff0c;用0表示两顶点不相连。任意顶点的度为邻接矩阵中该节点的行或列的…

pytorch案例代码-2

循环神经网络——基础知识 适合前后有联系的连续数据预测&#xff0c;比如天气预测、股市预测、自然语言等&#xff0c;而这些用DNN、CNN来做计算量就太大或者没法做&#xff0c;h0是先验&#xff0c;也可以前面接上CNNFC后面连上RNN&#xff0c;就可以完成图像到文本的转换&am…

AE VAE 代码和结果记录

Auto Encoder 在MNIST 上记录 直接上代码 import os os.chdir(os.path.dirname(__file__)) import torch import torch.nn as nn import torch.nn.functional as F import torchvision from torchvision import transforms from torchvision.utils import save_image from to…

数据结构-学习-01-线性表之顺序表-初始化、销毁、清理、获取长度、判断为空、获取元素等实现

一、测试环境 名称值cpu12th Gen Intel Core™ i7-12700H操作系统CentOS Linux release 7.9.2009 (Core)内存3G逻辑核数2gcc 版本4.8.5 20150623 二、个人理解 数据结构分为逻辑结构和物理结构&#xff08;也称为存储结构&#xff09;。 1、逻辑结构 逻辑结构又可以分为以下…

JS 事件

事件 事件是 JS 和 HTML 交互的桥梁。采用“观察者模式”&#xff0c;使用仅在事件发生时执行的监听器&#xff08;也叫处理程序&#xff09;订阅事件 事件流 事件流描述的是页面接收事件的顺序。分为 3 各阶段&#xff1a; 事件捕获&#xff1a;最先触发&#xff0c;可以做…

致敬经典 睛彩再现——AVS产业联盟和中国移动咪咕公司携手推动AVS3视频、音频标准

2022年11月14日&#xff0c;中国移动咪咕公司首发AVS3移动端规模化商用版本咪咕视频6.0.7.00&#xff0c;该版本下设的“致敬经典 睛彩再现”专区、以及“菁彩视听”双Vivid直播视角&#xff08;Audio Vivid & HDR Vivid&#xff09;&#xff0c;通过国家自主的AVS3、Audio…

回顾复习【矩阵分析】初等因子 和 矩阵的相似 || 由不变因子求初等因子 || 由初等因子和秩求Smith标准形(不变因子)

目录 1. 由不变因子,引出 初等因子的概念2. 【必看】例子:已知 不变因子,求初等因子。3.【必看】 例子:已知 秩和初等因子,求史密斯标准形(不变因子)4. 分块矩阵 初等因子的 求法5. 数字矩阵的相似 与 入-矩阵的等价1. 由不变因子,引出 初等因子的概念 例如,下面两个矩阵…

Kotlin 开发Android app(十):Android控件绑定ViewBinding

上一节中&#xff0c;我们知道了Android的布局&#xff0c;这种把界面和逻辑控制分开&#xff0c;是编程里很好的分离方式&#xff0c;也大大的解耦了界面和逻辑控制&#xff0c;使得编程的逻辑不在和界面挂钩。 有了界面的布局&#xff0c;我们需要把界面和代码部分进行绑定&…