C++速览之智能指针

news2024/11/15 2:17:28

1、存在的问题
c++ 把内存的控制权对程序员开放,让程序显式的控制内存,这样能够快速的定位到占用的内存,完成释放的工作。但是此举经常会引发一些问题,比如忘记释放内存。由于内存没有得到及时的回收、重复利用,所以在一些c++程序中,常会遇到程序突然退出、占用内存越来越多,最后不得不选择重启来恢复。造成这些现象的原因可以归纳为下面几种情况.

1.1 野指针
出现野指针的有几个地方 :
指针声明而未初始化,此时指针的将会随机指向
内存已经被释放、但指针仍然指向它。这时内存有可能被系统重新分配给程序使用,从而会导致无法估计的错误;

#include <iostream>

using namespace std;

int mian(){
    //1. 声明未初始化
    int *p1 ;
    cout << "打印p1: " << *p1 << endl;
    
    
    //2. 内存释放后,并没有置空 nullptr
    int *p = new int(55);

    cout << "释放前打印 :  " << *p << endl;

    delete  p ;
    cout << "释放后打印 :  " << *p << endl;
    
    return 0 ;
}

1.2 重复释放
程序试图释放已经释放过的内存,或者释放已经被重新分配过的内存,就会导致重复释放错误.

int main(){

    int *p = new int(4);

    //重复释放
    delete p;
    delete p;

    return 0 ;
}

1.3 内存泄漏
不再使用的内存,并没有释放,或者忘记释放,导致内存没有得到回收利用。 忘记调用delete

int main(){

    int *p = new int(4);
    
    
    //后面忘记调用delete p;

    return 0 ;
}

2、智能指针
为了解决普通指针的隐患问题,c++在98版本开始追加了智能指针的概念,并在后续的11版本中得到了提升
c++11标准用 unique_ptr | shared_ptr | weak_ptr 等指针来自动回收堆中分配的内存。智能指针的用法和原始指针用法一样,只是它多了些释放回收的机制罢了。

2.1 unique_ptr
unique_ptr 是一个独享所有权的智能指针,它提供了严格意义上的所有权
也就是只有这个指针能够访问这片空间,不允许拷贝,但是允许移动(转让所有权)

#include<iostream>
#include <memory>

using namespace std;

int main() {


    // 1. 创建unique_ptr对象,包装一个int类型指针
    unique_ptr<int> p(new int(10));

    // 2. 无法进行拷贝。编译错误
    //unique_ptr<int> p2 = p;
    cout << "-----1----" << *p << endl;

    // 3. 可以移动指针到p3. 则p不再拥有指针的控制权 p3 现在是唯一指针
    unique_ptr<int> p3 = move(p);
    cout << "-----2----" << *p3 << endl;

    // p 现在已经无法取值了。
    // cout << "-----3----" << *p << endl;

    // 可以使用reset显式释放内存。
    p3.reset();

    // 重新绑定新的指针
    p3.reset(new int(6));

    // 获取到包装的int类型指针
    int *p4 = p3.get();

    // 输出6
    cout << "-----4----" << "指针指向的值是:" << *p4 << endl;
    cout << "-----5----" << "*p3值是:" << *p3 << endl;

    return 0;
}

2.2 shared_ptr
shared_ptr : 允许多个智能指针共享同一块内存,由于并不是唯一指针,所以为了保证最后的释放回收,采用了计数处理,每一次的指向计数 + 1 , 每一次的reset会导致计数 -1 ,直到最终为0 ,内存才会最终被释放掉。 可以使用use_cout 来查看目前的指针个数

#include <iostream>
#include <memory>

using namespace std;

class Stu {
public:
    Stu() {
        cout << "执行构造函数" << endl;
    }

    ~Stu() {
        cout << "执行析构函数" << endl;
    }
};

int main() {
    shared_ptr<Stu> s1(new Stu());
    cout << " ---1--- = " << s1.use_count() << endl;  // 查看指向计数
    shared_ptr<Stu> s2 = s1;
    cout << " ---2--- = " << s1.use_count() << endl;  // 查看指向计数
    shared_ptr<Stu> s3 = s1;
    cout << " ---3--- = " << s1.use_count() << endl;  // 查看指向计数
    s1.reset();
    cout << " ---4--- = " << s3.use_count() << endl;  // 查看指向计数
    s2.reset();
    cout << " ---5--- = " << s3.use_count() << endl;  // 查看指向计数
    s3.reset();  // 至此全部解除指向 计数为0 。 会执行stu的析构函数
    cout << " ---6--- = " << s3.use_count() << endl;  // 查看指向计数

    return 0;
}

在这里插入图片描述
问题
对于引用计数法实现的计数,总是避免不了循环引用(或环形引用)的问题,即我中有你,你中有我,shared_ptr也不例外。 下面的例子就是,这是因为f和s内部的智能指针互相指向了对方,导致自己的引用计数一直为1,所以没有进行析构,这就造成了内存泄漏

#include <iostream>
#include <memory>

using namespace std;

class Son;

class Father {
private:
    shared_ptr<Son> son;
public:
    Father() {
        cout << "father 构造" << endl;
    }

    ~Father() {
        cout << "father 析构" << endl;
    }

    void setSon(shared_ptr<Son> son) {
        this->son = son;
    }
};

class Son {
private:
    shared_ptr<Father> father;
public:
    Son() {
        cout << "son 构造" << endl;
    }

    ~Son() {
        cout << "son 析构" << endl;
    }

    void setFather(shared_ptr<Father> father) {
        this->father = father;
    }
};


int main() {
    shared_ptr<Father> f(new Father());
    shared_ptr<Son> s(new Son());
    f->setSon(s);
    s->setFather(f);
}

在这里插入图片描述
解决办法
定义对象的时候,用shared_ptr指针;引用对象的地方,用weak_ptr指针

2.3 weak_ptr

#include <iostream>
#include <memory>

using namespace std;

class Son;

class Father {
private:
    weak_ptr<Son> son;
public:
    Father() {
        cout << "father 构造" << endl;
    }

    ~Father() {
        cout << "father 析构" << endl;
    }

    void setSon(shared_ptr<Son> son) {
        this->son = son;
    }
};

class Son {
private:
    weak_ptr<Father> father;
public:
    Son() {
        cout << "son 构造" << endl;
    }

    ~Son() {
        cout << "son 析构" << endl;
    }

    void setFather(shared_ptr<Father> father) {
        this->father = father;
    }
};


int main() {
    shared_ptr<Father> f(new Father());
    shared_ptr<Son> s(new Son());
    f->setSon(s);
    s->setFather(f);
}

在这里插入图片描述

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

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

相关文章

数字孪生智慧医院建设方案

2. 智慧医院背景与挑战 公立医院面临医疗保障能力、服务需求和DIP付费制度改革等变化&#xff0c;同时存在智慧医院建设中的“建多用不多”和系统间数据不互通等问题。 3. 解决方案与标准 通过“找方案”和“找标准”微信公众号&#xff0c;分享智慧城市、智慧医院等领域的解…

Flink程序部署与提交

前言 我们看门见山&#xff0c;生产环境一般用的是在YARN上面采用应用模式进行部署flink程序。实际生产中一般需要和资源管理平台&#xff08;如YARN&#xff09;结合起来&#xff0c;选择特定的模式来分配资源、部署应用。 部署模式 在一些应用场景中&#xff0c;对于集群资…

AIGC:clip-interrogator

文字生成图片是近年来多模态和大模型研究的热门方向&#xff0c;openai提出的CLIP提供了一个方法建立起了图片和文字的联系&#xff0c;但是只能做到给定一张图片选择给定文本语义最相近的那一个&#xff0c;实际项目开发中我们总是需要从一张图片获取描述&#xff0c;clip-int…

数据结构与算法概述(1/6)

目录 1. 引言 2. 数据结构的概念 2.1 什么是数据结构 2.2 数据结构的分类 2.3 数据存储结构 3. 算法的概念 3.1 什么是算法 3.2 算法的基本特性 3.3 算法的评价标准 3.4 算法的描述方法 4. 算法性能分析 4.1 时间复杂度的概念与分析 4.2 空间复杂度的概念与分析 4…

萨科微半导体入驻得捷

2024年8月1日&#xff0c;萨科微半导体公司宣布其高性能半导体产品成功入驻全球知名电子元器件采购平台得捷&#xff0c;这一合作不仅丰富了得捷的产品线&#xff0c;也标志着萨科微产品将借助得捷的广泛影响力加速出海&#xff0c;共同推动电子行业创新与发展&#xff0c;为全…

查看一个exe\dll文件的依赖项

方法 使用一个Dependencies工具&#xff0c;检测exe文件的所有依赖项 工具使用 下载压缩包之后解压&#xff0c;解压后如下图所示 在命令行中运行Dependencies.exe程序会得到帮助菜单 查询某exe的所有依赖项&#xff0c;使用命令 Dependencies.exe -chain <查询文件> …

uniapp app中使用柱状图 折线图 圆环图和饼图

实现思路 借助echarts.min.js 搭配l-echart进行配置 废话不多说上代码后自己百度了解配置项的意思就好 下面代码是折线图的 &#xff0c;柱状图和它一摸一样&#xff0c;只需要把line换成bar就好 <template><l-echart ref"chart"></l-echart> …

网络协议八 网络安全相关

网络通讯中的4种 安全问题 网络层- ARP欺骗 ARP欺骗的防护原理 DoS&#xff0c;DDoS 攻击 应用层 DNS 劫持 HTTP 协议的安全问题 单向散列函数&#xff0c;不可逆 MD4,MD5,SHA全家桶 可逆&#xff0c;对称加密 DES,3DES,AES DES,已经被破解&#xff0c;不建议使用 3DES AES 目前…

2024新型数字政府综合解决方案(三)

新型数字政府综合解决方案通过融合人工智能、大数据和云计算技术&#xff0c;建立了一个智能化、互联互通的政府服务平台&#xff0c;旨在提升政府服务效率与透明度。该方案通过全面数字化政务流程&#xff0c;实现数据的实时共享和自动化处理&#xff0c;使公众能够便捷地访问…

Qt作业合集

8.14作业 设置窗口&#xff0c;按钮&#xff0c;标签&#xff0c;行编辑器&#xff0c;实现快递速运登录页面 #include "mywidget.h"MyWidget::MyWidget(QWidget *parent): QWidget(parent) {//窗口//设置窗口的标题this->setWindowTitle("邮递系统")…

Flink on yarn 开发过程中遇到的问题

1. 任务启动报错Trying to access closed classloader. Exception in thread "Thread-5" java.lang.IllegalStateException: Trying to access closed classloader. Please check if you store classloaders directly or indirectly in static fields. If the st…

Qt QLabel标签制作弹框效果,3s后缓慢自动消失

效果图 初始化说明 void InitStatusTips() {if (NULL statusTips_) {return;}statusTips_->setFixedSize(300, 80);//固定大小statusTips_->move((width() - statusTips_->width()) / 2, height() - 30 - statusTips_->height());//移动位置statusTips_->setA…

汽车IVI中控OS Linux driver开发实操(二十四):I2C设备驱动的编写

在Linux驱动中I2C系统中主要包含以下几个成员: I2C adapter(即I2C适配器,用来控制各种I2C从设备,其驱动需要完成对适配器的完整描述,最主要的工作是需要完成i2c_algorithm结构体。这个结构体包含了此I2C控制器的数据传输具体实现,以及对外上报此设备所支持的功能类型。具…

钉钉虚拟位置打卡神器2024免费试用版下载-钉钉虚拟位置打卡神器

钉钉虚拟位置打卡神器是一款能够快速帮助用户修改定位的辅助&#xff0c;钉钉虚拟位置打卡免费版能够一键切换手机上班的打开地点&#xff0c;帮助打工人更好的应对公司&#xff0c;收获奖金&#xff01;软件不需要root就可以安装使用&#xff0c;并且体积也比较小&#xff0c;…

仿RabbitMq实现简易消息队列基础篇(future操作实现异步线程池)

TOC 介绍 std::future 是C11标准库中的一个模板类&#xff0c;他表示一个异步操作的结果&#xff0c;当我们在多线程编程中使用异步任务时&#xff0c;std::future可以帮助我们在需要的时候&#xff0c;获取任务的执行结果&#xff0c;std::future 的一个重要特性是能…

【Java学习】Stream流详解

所属专栏&#xff1a;Java学习 Stream流是JDK 8引入的一个概念&#xff0c;它提供了一种高效且表达力强的方式来处理数据集合&#xff08;如List、Set等&#xff09;或数组。Stream API可以以声明性方式&#xff08;指定做什么&#xff09;来处理数据序列。流操作可以被分为两大…

GD32 ADC配置跳坑

GD32 ADC配置跳坑 &#xff1a;时钟使能配置需在ADC前面 放在后面读取ADC值失败。 DMA配置放在ADC配置后面可以正常读取ADC的值 不同的模式选择可能会导致ADC存在读取失败的问题&#xff0c;红色部分是常用的模式&#xff0c;一般可以读取到相应的ADC的值 adc_software_trigge…

优雅谈大模型:Python编程篇

Python在机器学习领域的地位十分关键&#xff0c;虽然后面有Julia&#xff0c;Mojo等其他对手的挑战&#xff0c;然而Python拥有庞大的机器学习库和框架&#xff0c;尤其是生态系统比以往任何时候又强大了不少。从另外维度它和Java&#xff0c;Scala&#xff0c;Go&#xff0c;…

游戏安全入门-扫雷分析远程线程注入

前言 无论学习什么&#xff0c;首先&#xff0c;我们应该有个目标&#xff0c;那么入门windows游戏安全&#xff0c;脑海中浮现出来的一个游戏 – 扫雷&#xff0c;一款家喻户晓的游戏&#xff0c;虽然已经被大家分析的不能再透了&#xff0c;但是我觉得自己去分析一下还是极好…

适配器模式, 修饰器模式 与 代理模式

这三种模式, 感觉非常类似, 都是把核心类包一层, 在外部做一些额外的事情, 我还没发现他们之间具体的区别, 有想法的同学, 可以评论或者私聊我 适配器模式 简介: 就是在目标类外面包一层, 用以适配其他的模块,兼容整个程序框架 举个例子: 比如运动员, 中国运动员参加法国奥运…