C++智能指针之shared_ptr(保姆级教学)

news2024/10/6 12:35:56

目录

C++智能指针之shared_ptr

本文涉及所有程序

工作原理

使用方法

手动初始化

std::make_shared函数(C++14提出)

构造方法初始化

make_shared初始化

使用实例

shared_ptr常规操作

use_count();

unique();

reset();

get();

指定删除器

移动语义:std::move();


C++智能指针之shared_ptr

共享式指针:多个指针可以同时指向同一个对象(共享所有权,协同工作),当最后一个指针被销毁或者指向其他对象时,这个对象会被释放;

本文涉及所有程序

00_code.cpp

#include <iostream>
#include <memory>
#include <string>
using namespace std;

class A
{
public:
    A()
    {
        cout << "A" << endl;
    }
    ~A()
    {
        cout << "~A" << endl;
    }
};

void test(shared_ptr<A> pb)
{
    cout << "test func" << endl;
}

shared_ptr<A>test1()
{
    shared_ptr<A>temp(new A());
    return temp;
}

int main(int argc, char const* argv[])
{
    // int *p = new int(5);//裸指针

    // shared_ptr是类模板
    // shared_ptr<int> p = new int(5);//调用类型转换构造函数初始化 explicit不支持
    shared_ptr<int> p(new int(5));

    cout << *p << endl;

    // string *s = new string("hello world");
    shared_ptr<string> s(new string("hello world"));
    cout << *s << endl;

    // int num = 10;
    // shared_ptr<int>p2(&num);//报错:智能指针指向栈空间时,会导致释放两次空间或者对象

    shared_ptr<A> pa(new A()); //程序运行结束后会自动调用A的析构函数

    shared_ptr<A>result = test1();

    test(pa);

    shared_ptr<A> pa2(pa); // pa2,p同时指向同一个对象或者空间
    return 0;
}

01_code.cpp

#include <iostream>
#include <memory>
using namespace std;

int main(int argc, char const *argv[])
{
    shared_ptr<int> pi = make_shared<int>(5);

    cout << *pi << endl;

    shared_ptr<string> ps = make_shared<string>("hello world");
    cout << *ps << endl;

    //手动初始化和使用make_shared函数模板初始化有什么区别
    /**
    *推荐使用make_shared进行初始化;开销小,效率高(数据和引用计数存在同一段空间)
    *构造函数初始化:数据和引用计数不在同一段空间中,需要间接访问
    */
    return 0;
}

02_code.cpp

#include <iostream>
#include <memory>
using namespace std;

class A
{
public:
    A()
    {
        cout << "A" << endl;
    }

    A(int num):m_num(num)
    {
        cout <<"A int"<<endl;
    }

    A(const A&& other):m_num(other.m_num)
    {
        cout <<"A move int"<<endl;
    }

    ~A()
    {
        cout << "~A" << endl;
    }

public:
    int m_num;
};

void test(int *ptr)
{

}

void mydelete(A *pa)
{
    cout <<"call my delete" <<endl;
    delete []pa;
}
int main(int argc, char const *argv[])
{
#if 0
    //shared_ptr<A> pa = make_shared<A>(5); //隐式类型转换
    //cout<< pa->m_num<<endl;

    shared_ptr<A> pb = make_shared<A>(); 
    //cout<< pb->m_num<<endl;
    shared_ptr<A>pc(pb);
    cout << pb.use_count()<<endl;

    if(pb.unique())
    {
        cout << "pb is unique ptr" << endl;
    }

    // pb.reset();
    // if(pb == nullptr)
    // {
    //     cout << "pb is nullptr" << endl;  
    // }
    //shared_ptr<A>pd(new A(1));
    //pb.reset(pd);//reset不支持传入共享指针
    pb.reset(new A(6));
    cout <<  pb.use_count() <<endl;

    shared_ptr<int>pi(new int(19));
    test(pi.get());//void test(int *ptr);
    
#endif
    //指定删除器
    //shared_ptr<A>ptr(new A[3],mydelete);

    //shared_ptr<A[]>ptr(new A[3]);
    // shared_ptr<A>ptr(new A[3],[](A *a)
    // {
    //     delete []a;
    // });

    //shared_ptr<A>ptr(new A[3],default_delete<A[]>());

    shared_ptr<A>ptr(new A(6));
    shared_ptr<A>&&rptr = std::move(ptr);

    cout <<  ptr.use_count() <<endl;
    cout <<  rptr.use_count() <<endl;
    return 0;
}

03_code.cpp

#include <iostream>
using namespace std;

template <typename T>
class shared_ptr
{
public:
    shared_ptr(const shared_ptr<T> &other)
    {
        this->ptr = other.ptr;
        strong_ref++;
    }

    shared_ptr(const T *t)
    {
        this->ptr = t;
        strong_ref++;
    }

    T *get()
    {
        return ptr;
    }

    ~shared_ptr()
    {
        strong_ref--;
        if (strong_ref == 0)
        {
            delete ptr;
        }
    }

private:
    T *ptr;
    static int strong_ref;
};

template <typename T>
int shared_ptr<T>::strong_ref = 0;

int main(int argc, char const *argv[])
{

    return 0;
}

工作原理

引用计数增加/减少(原子操作)

  • 引用计数增加
    • 用一个智能指针初始化另一个智能指针
    • 函数传参:传递一个智能指针;
    • 函数返回值:返回一个智能指针
  • 引用计数减少
    • 给智能指针赋予新值,指向一个新对象
    • 局部的智能指针离开其作用域
    • =nullptr

定义:shared_ptr 智能指针变量名

使用方法

手动初始化

注:shared_ptr p = new int(10); //错误,

shared_ptr是explicit,不可以进行隐式类型转换,只能用构造函数直接初始化;

对于shared_ptr s(new string("hello world"));可以写成shared_ptr s("hello world"),因为"hello world"被当成了const char数组来使用(即字符指针)

std::make_shared函数(C++14提出)

auto p = std::make_shared("hello world");

error:auto p = std::make_shared(new int[]);

功能:在 堆内存中可以动态分配对象,并返回一个shared_ptr;

推荐方法,因为此方法安全,高效;

构造方法初始化

假如初始化化了两个智能智能sp1和sp2,它们指向的是同一个空间,使用构造函数进行初始化,指向的这个空间里面存放着强引用计数和弱引用计数以及实际存放变量的地址等

make_shared初始化

而使用函数模板进行初始化,指向的这个空间里面存放着强引用计数和弱引用计数以及实际存放变量等,它们都存储在同一段空间上,因此访问起来更高效

使用实例

1、引用计数增加/减少(原子操作)(以初始化方式一为例)

(1)通过VS2022进行调试,通过打断点,当执行完第一条指令后,引用计数变为1

当执行完第二条指令后,引用计数变为2

程序执行完,引用计数释放

(2)函数传参改变引用计数

初始化第一次,引用计数为1

函数传参,引用计数加1

函数执行完,新参释放,引用计数减1

初始化另一个智能指针,引用计数再次加1

(3)函数返回值为智能指针

函数内的智能指针释放,因此接返回值后,引用计数仍为1

2、make_shared函数进行初始化

对于自定义类型,可以使用隐式类型转换

make_shared也可以对普通变量或对象使用

shared_ptr常规操作

use_count();

功能:返回有多少个shared_ptr智能指针指向某对象;(引用计数的个数)

用途:主要用于调试

unique();

判断当前智能指针是否独享(只有它自己指向该对象)某个对象或者空间,独占返回true,否则返回false;

reset();

  • reset()无参使用:若该智能指针是独占某个对象,则释放该对象,并将智能指针置nullptr;若不独占,引用计数减1,并将该指针置nullptr;
  • reset()代参使用:若该智能指针是独占某个对象,则释放该对象,并将该指针指向新对象;若不独占,则将该指针指向的对象引用计数减1,并将该指针指向新对象;

实例:pb.reset();

pb.reset(new A(6));

get();

功能:获得智能指针中保存的指针(裸指针);主要为了适应C的接口

考虑到有些函数参数是裸指针并不是智能指针,所以需要将智能指针转化为裸指针;

指定删除器

原因:有些情况,默认删除器处理不了(shared_ptr管理动态数组),需要我们自己指定删除器;

对于图中示例,在进行释放的时候,只调用了delete ptr,只释放了一次。而我们希望的是调用delete []ptr将空间全部释放

解决方法1:自定义删除器函数

当默认的删除器不起作用时,需要自己指定删除器:void (*) T(T*);

在shared_ptr的构造函数中函数名作为第二个参数直接传入即可

解决方法2:通过指定类型,直接指定智能指针所执行的是一个数组

解决方法3:lambda表达式

解决方法4:可用标准库模板类default_delete做删除器(函数对象)

移动语义:std::move();

因为对象移动做的不是数据的拷贝,使用右值ptr会认为rptr是个临时变量,因此并不会改变原本引用计数

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

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

相关文章

确保浏览安全:使用SSH实施加密SOCKS5

在互联网上保护隐私和安全至关重要。使用SSH&#xff08;Secure Shell&#xff09;创建加密的SOCKS5代理是一种简单且有效的方法&#xff0c;可以确保您的网络流量在传输过程中受到保护。本文将向您介绍如何使用SSH实施加密SOCKS5&#xff0c;以提高您的浏览安全。 1、准备工作…

使用perf_analyzer和model-analyzer测试tritonserver的模型性能超详细完整版

导读 当我们在使用tritonserver部署模型之后&#xff0c;通常需要测试一下模型的服务QPS的能力&#xff0c;也就是1s我们的模型能处理多少的请求&#xff0c;也被称为吞吐量。 测试tritonserver模型服务的QPS通常有两种方法&#xff0c;一种是使用perf_analyzer 来测试&#…

递归/回溯/动规

1 动规-打家劫舍一 你是一个经验丰富的小偷&#xff0c;准备偷沿街的一排房间&#xff0c;每个房间都存有一定的现金&#xff0c;为了防止被发现&#xff0c;你不能偷相邻的两家&#xff0c;即&#xff0c;如果偷了第一家&#xff0c;就不能再偷第二家&#xff1b;如果偷了第二…

计算机竞赛 基于深度学习的中文情感分类 - 卷积神经网络 情感分类 情感分析 情感识别 评论情感分类

文章目录 1 前言2 情感文本分类2.1 参考论文2.2 输入层2.3 第一层卷积层&#xff1a;2.4 池化层&#xff1a;2.5 全连接softmax层&#xff1a;2.6 训练方案 3 实现3.1 sentence部分3.2 filters部分3.3 featuremaps部分3.4 1max部分3.5 concat1max部分3.6 关键代码 4 实现效果4.…

Dubbo2.0

前置 衡量网站性能指标&#xff1a; 响应时间&#xff1a;一次请求花费时间并发数&#xff1a;同时处理请求数 并发连接数&#xff1a;每秒建立总TCP数量请求数&#xff1a;QPS一秒请求数并发用户数&#xff1a;单位时间用户 吞吐量&#xff1a;单位时间处理请求数 QPS&#…

Web前端自动化测试Cypress实践总结

本文主要首先主要介绍了什么是自动化测试&#xff0c;接着对常用的自动化测试框架进行了对比分析&#xff0c;最后&#xff0c;介绍了如果将自动化测试框架Cypress运用在项目中。 一、自动化测试概述 为了保障软件质量&#xff0c;并减少重复性的测试工作&#xff0c;自动化测…

GaussDB数据库SQL系列-层次递归查询

目录 一、前言 二、GuassDB数据库层次递归查询概念 三、GaussDB数据库层次递归查询实验示例 1、创建实验表 2、sys_connect_by_path(col, separator) 3、connect_by_root(col) 4、WITH RECURSIVE 四、递归查询的优缺点 1、优点 2、缺点 五、总结 一、前言 层次递归…

中使用pack局管理器:管理器布置小部件

一、说明 在本教程中&#xff0c;我们将了解如何制作登录 UI。今天的教程将重点介绍如何使用 Tkinter 的pack布局管理器。 二、设计用户界面 什么是布局管理器&#xff1f;创建图形界面时&#xff0c;窗口中的小部件必须具有相对于彼此排列的方式。例如&#xff0c;可以使用微件…

Yolov5的tensorRT加速(python)

地址&#xff1a;https://github.com/wang-xinyu/tensorrtx/tree/master/yolov5 下载yolov5代码 方法一&#xff1a;使用torch2trt 安装torch2trt与tensorRT 参考博客&#xff1a;https://blog.csdn.net/dou3516/article/details/124538557 先从github拉取torch2trt源码 ht…

【C++ 二叉搜索树】

目录 1.什么是二叉搜索树2.构建二叉搜索树2.1首先搭建树的框架2.2搭建搜索树的框架 3.二叉搜索树的插入3.1非递归式插入3.2递归式插入 4.二叉搜索树的查找4.1非递归查找4.2递归查找 5.二叉搜索树的删除5.1非递归删除5.2递归删除 6.整个代码实现 1.什么是二叉搜索树 简单来讲就…

WebDAV之π-Disk派盘 + 小书匠

小书匠是一款功能丰富,强大的知识管理工具。全平台覆盖,离线数据存储,自定义数据服务器,所见即所得的 markdown 编辑体验。 小书匠提供了多种实用的编辑模式,例如:栏编辑、双栏编辑、三栏编辑、全屏写作、全屏阅读等。并且该软件还提供了许多有用的扩展语法,比如Latex公…

【前端demo】CSVJSON转换器 原生实现:CSV转换为JSON,JSON转换为CSV

文章目录 效果过程textareaTooltip提示工具按钮的min-width判断输入是否是CSV或JSONJSON与CSV样例JSON转为CSVCSV转为JSON不足之处 代码HTMLCSSJS 其他demo 效果 效果预览&#xff1a;CSV&JSON Converter (codepen.io) 参照的预览&#xff1a;JSV Converter(gpaiva00.git…

地毯16 CFR 1630/1631安全防火性易燃性测试

地毯的16 CFR 1630/1631安全防火性易燃性测试是一项重要的产品检测认证标准。该测试旨在评估地毯材料的防火性能&#xff0c;以确保其在使用过程中不会引发火灾或加剧火势。测试过程包括对地毯样品进行燃烧测试和燃烧后的评估。 根据16 CFR 1630标准&#xff0c;地毯样品将被暴…

检漏仪和高真空度控制技术在热管漏率和内部真空度测量中的应用

摘要&#xff1a;大量MEMS真空密封件具有小体积、高真空和无外接通气接口的特点&#xff0c;现有的各种检漏技术无法对其进行无损形式的漏率和内部真空度测量。基于压差法和高真空度恒定控制技术&#xff0c;本文提出了解决方案。方案的具体内容是将被测封装器件放置在一个比器…

elementui表格自定义表头的两种方法

表格自定义表头的方式 多选框表头换文字 请查看上篇博客&#xff1a;http://t.csdn.cn/69De2 文字换按钮 render-header render-header方法详情 Table-column Attributes 参数说明类型可选值默认值render-header列标题 Label 区域渲染使用的 FunctionFunction(h, { column, $in…

恒运资本:北向资金流出一定会跌吗?股票涨跌与什么有关?

北向资金被认为是A股商场的风向标&#xff0c;它的动向往往会影响投资者的心情。那么北向资金流出一定会跌吗&#xff1f;股票涨跌与什么有关&#xff1f;恒运资本也为大家准备了相关内容&#xff0c;以供参阅。 北向资金流出一定会跌吗&#xff1f; 北向资金流出并不一定意味…

快速解决 adb server version doesn‘t match this client

这个问题是由于电脑上安装了多个版本的adb工具&#xff0c;客户端和服务端的版本不一致&#xff0c;无法正常通信导致。最快的解决方法就是将Android SDK中adb复制到系统目录下。 操作步骤如下&#xff1a; 1. 查看adb版本和路径 执行adb version&#xff0c;如下&#xff0…

手机无人直播软件在苹果iOS系统中能使用吗?

在现代社交媒体的时代&#xff0c;直播带货已经成为了一种热门的销售途径。通过直播&#xff0c;人们可以远程分享自己的商品&#xff0c;与观众进行互动&#xff0c;增强沟通和参与感。而如今&#xff0c;手机无人直播软件更是成为了直播带货领域的一项火爆的技术。那么&#…

渗透测试——安全漏洞扫描工具APPScan的安装与基本使用步骤

前言 HCL AppScan Standard是安全专家和渗透测试者设计的动态应用程序安全测试工具&#xff0c;AppScan使用强大的扫描引擎&#xff0c;会自动检索目标应用程序并测试漏洞。测试结果按优先级排列&#xff0c;允许操作员快速分类问题、发现最关键的漏洞。每个检测到的问题都可以…

Python 自学:使用线程模块同时运行代码 Threading

1. 以下代码中&#xff0c;程序会等一个函数执行完毕才执行下一个函数。 import timestart time.perf_counter()def do_something():print(Sleeping 1 second...)time.sleep(1)print(Done Sleeping...)do_something() do_something()finish time.perf_counter()print(fFinis…