C++ 智能指针 : auto_ptr 、unique_ptr、 shared_ptr、 weak_ptr

news2024/9/19 23:58:14

1、智能指针设计初衷:

        智能指针实际是类,超过类的作用域后,析构函数会自动回收资源,为程序员管理申请的堆内存,避免内存泄漏

2、C++ 智能指针种类: 

auto_ptr

(C++98 的⽅案,C11 已抛弃)采⽤所有权模式。auto_ptr构造时取得某个对象的所有权,在析构时释放该对象。我们实际上是创建一个auto_ptr<Type>类型的局部对象,该局部对象析构时,会将自身所拥有的指针空间释放,所以不会有内存泄露。auto_ptr不支持new 数组。

注意:不要使用auto_ptr 作为函数入参因为函数的形参其实是拷贝,然而智能指针拷贝的过程,会导致其拥有权发生转移,你传入的实参拥有权会转移到拷贝后的形参上,形参在函数运行的生命周期结束后,就会析构,导致你之前实参拥有的(主程序的对象、内存)内存被释放,导致致命的执行错误。

结论:如果必须需要使用智能指针作为函数入参,使用const 引用

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

int main()
{
    //auto_ptr 通过构造函数初始化
    int*ptr1 = new (std::nothrow)int(4);
    if (nullptr == ptr1)
    {
        cout<<"new failed !"<<endl;
        return -1;
    }

    auto_ptr<int> a_ptr1(ptr1);
    auto_ptr<int> a_ptr2(new int(5));
    cout << "a_ptr1 data : "<<*a_ptr1 <<endl;
    cout << "a_ptr2 data : "<<*a_ptr2 <<endl;

    //一块内存只能由一个auto_ptr独享,拷贝构造或赋值时都会发生拥有权转移
    //拷贝构造,a_ptr1失去对ptr1内存的所有权,a_ptr3拥有,负责内存的销毁
    auto_ptr<int> a_ptr3(a_ptr1);

    //赋值
    a_ptr2 = a_ptr3;
    //cout << "拷贝构造后 a_ptr1 data : "<<*a_ptr1 <<endl;
    cout << "赋值后 a_ptr2 data : "<<*a_ptr2 <<endl;
    //cout << "赋值后 a_ptr3 data : "<<*a_ptr3 <<endl;

    //智能指针会默认初始化为0
    auto_ptr<int> a_ptr4;

    //禁止一个对象(内存)被多个auto_ptr拥有
    /* int *data = new (std::nothrow)int(5);
    auto_ptr<int> a_ptr5 = auto_ptr<int>(data);
    auto_ptr<int> a_ptr6 = auto_ptr<int>(data);
    cout << "a_ptr5 data : "<<*a_ptr5 <<endl;
    cout << "a_ptr6 data : "<<*a_ptr6 <<endl; */

    //常用函数
    //get()获取auto_ptr拥有的内存(对象)的地址
    int *data1 =  new (std::nothrow)int(5);
    auto_ptr<int> a_ptr7(data1);
    cout << "the add of data1: " << data1 << endl;
    cout << "the get() return of a_ptr7: " << a_ptr7.get() << endl;

    //reset 重置auto_ptr拥有的内存(对象),重置的过程会释放auto_ptr先拥有的内存int(5)
    cout << "the data of a_ptr7: " << *a_ptr7 << endl;
    a_ptr7.reset(new (std::nothrow)int(6));
    cout << "the data of a_ptr7 after reset: " << *a_ptr7 << endl;

    return 0;
    
}

unique_ptr

unique_ptr 实现独占式拥有或严格拥有概念,保证同⼀时间内只有⼀个智能指针可以指向该对 象。它对于避免资源泄露特别有⽤。

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

int main()
{

    /* unique_ptr */
    int *p = new int(5);
    unique_ptr<int>p1(p);
    //std::unique_ptr<int>p2=p1;// 编译会出错
    //move转移所有权, 现在那块内存归p3所有, p1成为无效的指针.
    unique_ptr<int>p3=move(p1);
    cout << "the add of p: " << p <<" | "<<  " the data of p: " << *p << endl;
    cout << "the add of p3: " << p3.get() <<" | "<< " the data of p3: " << *p3 <<endl;
    //reset释放内存.
    p3.reset();
    //无效
    //p1.reset();

    //默认初始为nullptr
    unique_ptr<int>p4;
    cout << "the add of p4 : " << (p4 ? "not nullptr" : "nullptr") << endl;

    //release释放智能指针的内存拥有权
    int *a = new int(4);
    p4 = (unique_ptr<int>)(a);
    int *p5 = p4.release();
    cout << "the add of a: " << a <<" | "<< "the data of a: " << *a <<endl;
    cout << "the add of p5: " << p5 <<" | "<< "the data of p5: " << *p5 <<endl;
    delete p5;
    p5 = nullptr;

    //swap交换内存
    unique_ptr<int> p6(new int (6)); 
    unique_ptr<int> p7(new int (7));
    p6.swap(p7);
    cout << "the data of p6: " << *p6 << " | " <<"the data of p7: " << *p7 << endl;

    return 0;
    
}

shared_ptr

共享型智能指针,一块内存可被多个shared_ptr 引用,内部采用引用计数管理,计数为0时,则删除智能指针,回收资源。

注意:

1、不要使用同一个原始指针构造 shared_ptr,如果需要构建多个shared_ptr管理同一块内存,使用一个已存在的shared_ptr 进行创建。

2、shared_ptr 不能相互引用,会导致引用计数无法下降到0,资源无法被回收,造成内存泄漏

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

int main()
{
    
    /* shared_ptr */
    //使用 make_shared 创建空对象
	shared_ptr<int> sha_p1 = make_shared<int>();
    *sha_p1 = 1;

    //use_count 查看内存引用计数
    cout << "the use count = " << sha_p1.use_count() << endl; //1

    //无参reset 计数重置为0
    sha_p1.reset();
    cout << "the use count = " << sha_p1.use_count() << endl; //0

    //有参reset 计数为1
    sha_p1.reset(new int(2));
    cout << "the use count = " << sha_p1.use_count() << endl; //1

    //置nullptr 计数为0
    sha_p1 = nullptr;
    cout << "the use count = " << sha_p1.use_count() << endl; //0

    //不要用原始指针初始化多个智能指针
    int *num = new int(3);
    shared_ptr<int> sha_p2(num);
    
    shared_ptr<int> sha_p3(sha_p2);  // 正确使用方法
    shared_ptr<int> sha_p4(num);     // 不推荐
    //假如使用原始指针num创建了sha_p2,sha_p4,当sha_p2超出作用域时会调用
    //delete释放num内存,此时num成了悬空指针,当sha_p4超出作用域再次delete的时候就可能会出错

    cout << "sha_p2 use count = " << sha_p2.use_count() << endl; // 输出 2
    cout << "sha_p3 use count = " << sha_p3.use_count() << endl; // 输出 2
    cout << "sha_p4 use count = " << sha_p4.use_count() << endl; // 输出 1

    return 0;

}

weak_ptr

weak_ptr 是为了配合 shared_ptr 而引入的一种智能指针,可以解决shared_ptr 循环引用的问题,weak_ptr,由于是弱共享,weak_ptr 的创建并不会影响 shared_ptr 的引用计数值。

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

class ClassB;
 
class ClassA
{
public:
 ClassA() { cout << "ClassA Constructor Success" << endl; }
 ~ClassA() { cout << "ClassA Destructor Success" << endl; }
 weak_ptr<ClassB> pb; // 在A中引用B
};
 
class ClassB
{
public:
 ClassB() { cout << "ClassB Constructor Success" << endl; }
 ~ClassB() { cout << "ClassB Destructor Success" << endl; }
 weak_ptr<ClassA> pa; // 在B中引用A
};

int main()
{
   
    /* weak_ptr*/
    shared_ptr<ClassA> ca_ptr = make_shared<ClassA>();
    shared_ptr<ClassB> cb_ptr = make_shared<ClassB>();
    ca_ptr->pb = cb_ptr;
    cb_ptr->pa = ca_ptr;
    cout << "the use count of ca_ptr = " << ca_ptr.use_count() << endl; //1
    cout << "the use count of cb_ptr = " << cb_ptr.use_count() << endl; //1
    //对象A 、B都被析构了

    return 0;

}

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

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

相关文章

5G NR标准: 第17章 LTE/NR互通和共存

第17章 LTE/NR互通和共存 新一代移动通信技术的初始部署通常发生在交通密度高、对新服务能力要求高的地区。 然后是逐渐的进一步扩建&#xff0c;根据运营商的策略&#xff0c;扩建的速度或快或慢。 在随后的逐步部署过程中&#xff0c;新技术和传统技术的混合将提供对运营商…

实习------SpringBoot 框架

Spring Boot 是什么 (了解)Spring Boot 是 Spring 开源组织下的子项目&#xff0c;其设计目的是专注于Spring应用的开发&#xff0c;开发人员可以把更多的精力放在业务代码上&#xff0c;而无需过多关注XML的配置&#xff0c;从而简化Spring应用开发&#xff0c;提高开发效率Sp…

【Linux网络管理】之ip、nmcli

文章目录一、验证网络配置1. ip link命令将列出系统上可用的所有网络接口2. ip命令查看设备和地址信息3. ip命令显示性能统计信息4. ip命令以及route选项来显示路由信息5. 添加-6选项可显示IPv6路由器二、查看联网信息1.nmcli dev status命令可显示所有网络设备的状态2.nmcli c…

JDBC基础内容

JDBC的概念&#xff1a; JDBC就是使用java语言操作关系数据库的一套API&#xff0c;全称为(java DataBase Connectivity)-----java数据库连接. JDBC的本质&#xff1a; 是由sun公司定义的一套操作所有关系型数据库的规则&#xff0c;即接口&#xff0c;各个数据库厂商去实现…

更智能行车记录仪,4K画质超清晰,凌度行车记录仪 4K版上手

行车记录仪是很多车友的必需品&#xff0c;这两年行车记录仪的提升非常明显&#xff0c;如今市面上已经能够找到很多4K分辨率的产品了&#xff0c;相比于早期只要有480P分辨率的记录仪&#xff0c;清晰度和可靠性都更加出色&#xff0c;确实有升级的必要。 这两天我尝试了一款华…

P2141 [NOIP2014 普及组] 珠心算测验————C++

题目 [NOIP2014 普及组] 珠心算测验 题目描述 珠心算是一种通过在脑中模拟算盘变化来完成快速运算的一种计算技术。珠心算训练&#xff0c;既能够开发智力&#xff0c;又能够为日常生活带来很多便利&#xff0c;因而在很多学校得到普及。 某学校的珠心算老师采用一种快速考…

基于python机器学习及深度学习在空间模拟与时间预测领域中的实践技术应用

机器学习 理论知识 1.1.1 课程目标 了解机器学习的发展历史、计算原理、基本定义&#xff0c;熟悉机器学习方法的分类&#xff0c;常用机器学习方法&#xff0c; 以及模型的评估与选择&#xff1b;熟悉数据预处理的流程&#xff0c;掌握 python 程序包的使用&#xff1b;理解机…

美国培生教育集团下PYP Readers系列的音乐是怎样制造的,读了吗?

很多小朋友都对音乐很感兴趣。 TA可能会喜欢Pop Music , 可能会喜欢Rap Music&#xff0c; 也可能会喜欢Rock&Roll...... 那么TA可能会想&#xff0c;这些音乐到底是怎样产生的呢&#xff1f; 没错&#xff0c;乐器&#xff08;Instrument&#xff09;&#xff01; 好书推荐…

【YOLO系列】YOLO V1论文总结

目标检测论文总结 【RCNN系列】 RCNN Fast RCNN Faster RCNN 【YOLO系列】 YOLO V1 文章目录目标检测论文总结前言一、Pipeline二、模型设计1.训练阶段2.损失函数2.1.框回归损失2.2.置信度损失2.3.分类损失总结前言 一些经典论文的总结。 一、Pipeline YOLO的pipeline很简单…

排查 Hive 任务导致磁盘写满的过程

计算的中间结果会写到 HDFS&#xff0c;如果用户写的SQL 有问题。如用户的 SQL 如下&#xff1a; select * from A join B where a.dt20230101 and b.dt20230101&#xff0c;如 A 表 dt20230101 分区 1000万行&#xff0c;B 表 dt20230101 有 1万行记录。由于 SQL 没有关联条件…

Druid 连接池技术的使用

文章目录官网链接连接性能消耗问题分析数据库连接池的作用市面常见连接池产品和对比国货之光druid连接池使用导入druid依赖硬编码方式&#xff08;了解&#xff09;软编码方式druid配置(了解)官网链接 http://www.apache-druid.cn/GettingStarted/chapter-1.html 连接性能消耗…

[Vue组件及组件之间的通信]一.Vue脚手架的使用;二.Vue的组件和组件之间的通信

目录 一.Vue脚手架的使用 1.通过命令行使用vue-cli的指令创建&#xff1a;vue init webpack 项目名称 2使用webStorm软件&#xff1a;本质仍然使用vue脚手架 3.使用vue ui创建&#xff1a;vue提供的图形化的操作界面 二.Vue的组件和组件之间的通信 1.组件&#xff1a;是vu…

数字签名与签名验证过程

1.1 生成数字签名 1 利用RSA算法生成公钥、私钥。私钥由密钥持有者自主保存&#xff0c;公钥可对外发布。 2 准备好待签名的文档。 3 利用哈希算法&#xff08;HASH&#xff09;&#xff0c;生成待签名文档的摘要。&#xff08;文档摘要&#xff09; 4 利用签名者的私钥&am…

谷粒学院——第十八章、统计分析

准备工作 需求分析 1、统计在线教育项目中&#xff0c;每一天有多少注册人数 2、把统计出来的注册人数&#xff0c;使用图表显示出来 创建数据库表 创建工程 service_statistics 配置文件 # 服务端口 server.port8008# 服务名 spring.application.nameservice-statistic…

Python基础(二十一):面向对象深入了解

文章目录 面向对象深入了解 一、魔法方法 1、__init__() 2、__str__() 3、__del__()

数据结构入门——二叉树(C语言实现)

数据结构入门——二叉树一. 树概念及结构1.1 树的概念1.2 树的相关概念1.3 树的表示1.4 树的应用二. 二叉树概念及结构2.1 二叉树的概念2.2 特殊的二叉树2.3 二叉树的性质2.4 二叉树的存储结构三. 二叉树的顺序结构及其实现(堆的实现)3.1 二叉树的顺序结构3.2 堆的实现(以大堆为…

要想宝宝吃得好,粮仓就要保护好,做好3点保护粮仓,防止皲裂

众所周知&#xff0c;母乳喂养的妈妈并不容易&#xff0c;因为母乳喂养也有很多“难题”&#xff0c;也会很痛&#xff0c;比如开奶痛、挂奶、堵奶等&#xff0c;疼痛的程度不亚于子宫收缩。还有一个&#xff0c;牛奶&#xff0c;牛奶。.头皲裂的时候&#xff0c;真的是含泪喂奶…

Python实现可视化大屏数据

参考网址如下&#xff1a; 【Python】全网最新最全Pyecharts可视化教程(三)&#xff1a;制作多个子图_51CTO博客_python数据可视化pyecharts使用pyecharts拖拉&#xff0c;拽&#xff0c;创建大屏展示 - 简书 (jianshu.com) 智慧大屏是如何实现数据可视化的&#xff1f; - 知…

调查问卷考试问卷创建生成工具助手小程序开发

调查问卷考试问卷创建生成工具助手小程序开发 问卷调查考试软件&#xff0c;可以自由让每一个用户自由发起调查问卷、考试问卷。发布的问卷允许控制问卷的搜集、回答等各个环节的设置&#xff0c;同时支持系统模板问卷&#xff0c;选用模板问卷后可以一键创建属于自己的问卷&a…

JVM基础知识总结

日常工作中接触到的jvm相关的知识&#xff0c;和jvm相关书籍中汇总总结一下jvm相关基础知识&#xff0c;作为对jvm的了解。 文章目录jvm运行时数据区域程序计数器java虚拟机栈堆heap非堆内存 nonheap方法区直接内存 Direct Memory类加载机制类的加载过程类加载器加载过程的详细…