C++智能指针及简单实现

news2024/11/27 8:37:17

C++智能指针

  • 堆内存、栈内存与静态内存
    • 静态内存
    • 栈内存
    • 堆内存
  • 动态内存管理
    • new、delete运算符
    • 智能指针
    • 实现智能指针
  • shared_ptr
    • 智能指针的线程安全问题
    • 解决
  • unique_ptr
  • weak_ptr
    • 循环引用
  • 思维导图
  • 本模块思路

动态内存管理 - cppreference.com

堆内存、栈内存与静态内存

静态内存

  • **保存局部static对象。**例如统计函数本身被调用了多少次,可以在函数体内定义一个static对象,不会随函数体结束而销毁,更特别的是,只会在第一次使用时初始化。
  • 保存类的static成员。类的static成员仅与类有关,而非与类的每个对象关联,更重要的是,static成员的更新会应用到类的每个对象。
  • 全局变量

栈内存

  • 函数内的非静态对象

堆内存

  • 保存动态分配的对象

动态内存管理

new、delete运算符

  • new 在申请内存的同时,还会调用对象的构造函数,返回指向该对象的指针
  • delete 在释放内存之前,会调用对象的析构函数
  • 易产生问题:内存泄漏(忘记释放)、引用非法内存指针(释放早了)

智能指针

位于memory头文件,智能指针是模板类(类似vector),所以相当于把指针包成一个类,添加一些成员(use_count等)的同时使得指针拥有了构造函数和析构函数,这样我们只需要关注内存的申请,内存的释放则由程序自动完成。

下面先手动实现一个智能指针:

实现智能指针

smartptr.h

#pragma once
#ifndef SMART_PTR_H
#define SMART_PTR_H
#include<iostream>

template<typename T>
class SmartPtr
{
public:
    //默认构造函数
    SmartPtr() :ptr(nullptr), count(nullptr) {}
    //传指针构造函数
    SmartPtr(T* _ptr) : ptr(_ptr), count(nullptr) 
    {
        if (_ptr)count = new int(1);
    }
    //拷贝构造函数
    SmartPtr(const SmartPtr& smp)
    {
        ptr = smp.ptr;
        count = smp.count;
        if (count)(*count)++;
    }
    //析构函数
    ~SmartPtr()
    {
        reset();
    }
    //重载=运算符
    SmartPtr& operator=(const SmartPtr& smp)
    {
        if (this == &smp)//指向同一块共享内存
        {
            return *this;
        }

        reset();//不一块内存,递减count
        this->ptr = smp.ptr;
        this->count = smp.count;
        if (count)(*count)++;

        return *this;
    }
    //重载*运算符
    T operator*() {
        return *(this->ptr);
    }
    //重载->运算符
    T* operator->() {
        return this->ptr;
    }
    //取出原始指针
    T* get()
    {
        return this->ptr;
    }
    //检查是否只有一个共享指针
    bool unique()
    {
        return *count == 1;
    }
    //返回计数器
    int use_count()
    {
        return *count;
    }
    //析构时削减共享计数并检查
    void reset()
    {
        if (count)
        {
            (*count)--;
            if (*count == 0)
            {
                delete this->ptr;
                delete this->count;
            }
        }
    }

private:
    T* ptr;
    int* count;
};

#endif // !SMART_PTR_H

main.cpp

#include<iostream>
#include<memory>
#include"smartptr.h"

using std::make_shared;
using std::shared_ptr;
using std::cout;
using std::endl;

int main()
{
    //对比int *sp = 100;
    auto sp = make_shared<int>(100);
    auto mysp = SmartPtr<int>(new int(100));
    cout << "shared_ptr:" << *sp << endl;
    cout << "My shared_ptr:" << *mysp << endl;
    cout << "shared_ptr use_count:" << sp.use_count() << endl;
    cout << "My shared_ptr use_count:" << mysp.use_count() << endl;
    cout << "shared_ptr unqiue:" << sp.unique() << endl;
    cout << "My shared_ptr unique:" << mysp.unique() << endl;
    auto sp2 = shared_ptr<int>(sp);
    auto mysp2 = SmartPtr<int>(mysp);
    cout << "shared_ptr use_count:" << sp.use_count() << endl;
    cout << "My shared_ptr use_count:" << mysp.use_count() << endl;
    cout << "shared_ptr unqiue:" << sp.unique() << endl;
    cout << "My shared_ptr unique:" << mysp.unique() << endl;

    auto p = sp.get();
    auto myp = mysp.get();

    auto sp3 = make_shared<int>();
    auto mysp3 = SmartPtr<int>();
    sp3 = sp2;
    mysp3 = mysp2;
    cout << "shared_ptr use_count:" << sp.use_count() << endl;
    cout << "My shared_ptr use_count:" << mysp.use_count() << endl;
    cout << "shared_ptr unqiue:" << sp.unique() << endl;
    cout << "My shared_ptr unique:" << mysp.unique() << endl;

    auto sp4 = make_shared<int>(10);
    auto mysp4 = SmartPtr<int>(new int(10));
    mysp3 = mysp4;
    sp3 = sp4;
    cout << "shared_ptr use_count:" << sp.use_count() << endl;
    cout << "My shared_ptr use_count:" << mysp.use_count() << endl;
    cout << "shared_ptr unqiue:" << sp.unique() << endl;
    cout << "My shared_ptr unique:" << mysp.unique() << endl;

    getchar();
    return 0;
}

shared_ptr

在这里插入图片描述
智能指针会将new和delete的过程自动化,它本质上是一个原始指针的包装。
一个允许多个对象指向同一块内存的指针对象,会对该内存地址的引用数量进行计数,shared ptr中除了有一个指针,指向所管理数据的地址。还有一个指针指向一个控制块的地址,里面存放了所管理数据的数量 (常说的引用计数) 、weak ptr的数量、删除器、分配器等,这里控制块是线程安全的,但是所管理数据的地址不是,
在这里插入图片描述

智能指针的线程安全问题

多个线程同时修改同一个shared_ptr对象,线程不安全

两个线程中智能指针的引用计数同时++或–,这个操作不是原子的,原因可看i++是原子操作吗,引用计数原来是1,++了两次,可能还是2.这样引用计数就错乱了,具体可以看C++ 智能指针线程安全的问题中的例子。

所谓原子操作是指不会被线程调度机制打断的操作;这种操作一旦开始,就一直运行到结束,中间不会有任何context switch (切换到另一个线程)。 通常所说的原子操作包括对非long和double型的primitive进行赋值,以及返回这两者之外的primitive。

解决

加锁,但是更好的方法是尽量不要多线程地改变指针指向

也可以用原子智能指针:C++ 20 引入了原子智能指针std::atomic<std::shared_ptr>

unique_ptr

unique_ptr 不共享它所管理的对象,两个unique_ptr不能指向同一个对象,unique”占有“它的指针,不能赋值和拷贝,智能释放指针或是对控制权进行转移。

使用时,先include
在这里插入图片描述
这样我们就定义了一个名为entity的智能指针,一个更好的出于防止构造函数抛出异常的定义是:
在这里插入图片描述

weak_ptr

使用weak_ptr复制shared_ptr时不会增加引用计数,这是它最大的特点
这种指针不具有指针功能,最大作用在于解决循环引用的问题

循环引用

可以理解为形成了循环链表,A要释放就要先释放指向A的B,B要释放就要先释放指向B的A,这种时候会造成内存泄漏,此时将其中一个改成weak_ptr,然后在需要获取操作权的时候使用weak_ptr.lock()返回指向共享内存空间的shared_ptr()即可,详见c++ weak ptr解除指针循环引用

思维导图

在这里插入图片描述

本模块思路

在这里插入图片描述

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

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

相关文章

会声会影2024购买多少钱 会声会影在哪里购买

掌握视频编辑技术&#xff0c;能为我们的工作和生活带来很多帮助。例如&#xff1a;将我们精心编辑的视频&#xff0c;上传到抖音、快手等平台进行变现&#xff1b;通过天马行空的视频创意&#xff0c;摇身一变成为B站up主。因此&#xff0c;拥有一款像会声会影这样的视频编辑软…

ATFX汇市:澳洲联储12月决议维持基准利率不变,AUDUSD短线大跌

ATFX汇市&#xff1a;12月5日&#xff0c;澳洲联储利率决议结果显示&#xff0c;将现金利率目标维持在4.35%不变 金融衍生品结算余额的支付利率维持在4.25%不变&#xff0c;符合市场预期。政策声明中提到&#xff1a;更加确定通货膨胀将会在合理的时间内返回目标&#xff1b;劳…

8.HTTP工作原理

HTTP是什么 HTTP工作原理 HTTP协议的请求类型和响应状态码 总结 1.HTTP是什么 HTTP超文本传输协议就是在一个网络中上传下载文件的一套规则 2.HTTP工作原理 HTTP超文本传输协议的本质是TCP通信&#xff0c;链接—>请求—>响应—>断开 3.HTTP协议的请求类型和响应状…

深入理解强化学习——马尔可夫决策过程:占用度量-[基础知识]

分类目录&#xff1a;《深入理解强化学习》总目录 文章《深入理解强化学习——马尔可夫决策过程&#xff1a;贝尔曼期望方程-[基础知识]》中提到&#xff0c;不同策略的价值函数是不一样的。这是因为对于同一个马尔可夫决策过程&#xff0c;不同策略会访问到的状态的概率分布是…

ssm的网上奶茶店系统(有报告)。Javaee项目。

演示视频&#xff1a; ssm的网上奶茶店系统&#xff08;有报告&#xff09;。Javaee项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系结构&#xff0c;通过Spring SpringMvc Mybat…

相关基础知识

本文引注&#xff1a; https://zhuanlan.zhihu.com/p/447221519 1.方差 2.自协方差矩阵 3.自相关矩阵 4.互协方差矩阵 5.互相关矩阵 6.相关系数 7.自相关函数、自协方差函数与功率谱密度 8.互相关函数、互协方差函数与互功率谱密度

指针基础知识超详细讲解(C语言)(上)

还在为不懂指针而感到烦恼么&#xff1f;那还在等什么呢快来看看吧&#xff01;冲冲冲&#xff01; 文章目录 1&#xff0c;认识指针2&#xff0c;指针变量指针变量的定义指针变量的大小const修饰指针变量 3&#xff0c;指针的运算4, 野指针概念成因规避 5&#xff0c; assert…

Sketch利器大公开:十款使用率超高的懒人插件汇总

在设计领域&#xff0c;Sketch以其高效小巧的优势赢得了众多设计团队的青睐&#xff0c;帮助全球设计师创作了许多令人难以置信的作品。在使用Sketch的过程中&#xff0c;辅助使用一些Sketch插件可以让我们更有效地完成设计任务。在本文中&#xff0c;我们将揭示大厂设计师的收…

Mysql主从复制原理和三种复制:异步、同步、半同步(主的binlog、从的relaylog)

借鉴&#xff1a;https://zhuanlan.zhihu.com/p/651625709 https://www.jb51.net/article/258847.htm 一&#xff1a;Mysql主从复制原理 MySQL的复制原理分三步 1.在主库上把数据更改&#xff0c;记录到二进制日志&#xff08;Binary Log&#xff09;中。 2.从库将主库上的日志…

分享86个选项卡TABJS特效,总有一款适合您

分享86个选项卡TABJS特效&#xff0c;总有一款适合您 86个选项卡TABJS特效下载链接&#xff1a;https://pan.baidu.com/s/1NBtPP2tT5YQqi6c744tCqg?pwd6666 提取码&#xff1a;6666 Python采集代码下载链接&#xff1a;采集代码.zip - 蓝奏云 学习知识费力气&#xff0…

软件测试面试题解析--什么题是必问的?

设计测试用例的主要方法有哪些&#xff1f;简述一下缺陷的生命周期&#xff1f;测试流程&#xff1f;项目流程&#xff1f;验收测试中和β测试区别&#xff1f;如何维护测试用例&#xff1f;每天测多少用例怎么分配的测试的一天能找多少bug你在上一家公司&#xff0c;写没写过测…

【Maven】更新依赖索引

有时候给idea配置完maven仓库信息后&#xff0c;在idea中依然搜索不到仓库中的jar包。这是因为仓库中的jar包索引尚未更新到idea中。这个时候我们就需要更新idea中maven的索引了&#xff0c;具体做法如下&#xff1a; 打开设置----搜索maven----Repositories----选中本地仓库-…

装修流程篇

装修流程 https://www.xiaohongshu.com/explore/627ba70d00000000210357b3 https://www.xiaohongshu.com/explore/63b6bc0c000000002203776f 半包装修流程 https://www.xiaohongshu.com/explore/64e5ea3b0000000003021711 户型图 效果 https://www.xiaohongshu.com/ex…

P5 Linux 标准C库函数

目录 前言 01 标准输入、标准输出和标准错误 02 打开文件 fopen() 03 新建文件的权限 04 fclose()关闭文件 05 读文件和写文件 06 库函数 fseek 定位 6.1 lseek的使用 07 ftell()函数 前言 &#x1f3ac; 个人主页&#xff1a;ChenPi &#x1f43b;推荐专栏1: 《C_Chen…

sql 读写注入

root高权限读写注入 load_file 读取文件 大姐我真是整了半天都是nullnullnull缝子 结果看了半天这个my.ini是被隐藏的大哥 load_file()读取文件结果为null_mysql load_file返回null解决办法_黑小薛的博客-CSDN博客 终于读出来了 此时参数值系统变量 secure_file_priv已经被修…

使用DevEco Studio时遇见的错误情况与问题

第一个 问题:打开项目文件,控制台报错 hvigor ERROR: Unable to find sdk.dir in local.properties or OHOS_BASE_SDK_HOME in the system environment path. 解决办法:在项目根目录中打开local.properties。如果没有local.properties,自己创建。 在local.properties中填…

[ROS2] --- 手动编写一个节点

1 准备工作 1.1 创建/编译工作空间 创建工作空间 mkdir -p ~/dev_ws/src cd ~/dev_ws/src1. 2 创建功能包 ros2 pkg create learning01_write_a_node --build-type ament_cmake --dependencies rclcpp可以看到目录结构如下&#xff1a; 1. 3 创建节点 在learing01_write…

234 回文链表

解题思路&#xff1a; \qquad 由于链表的结构特点&#xff0c;访问链表中的元素的时间复杂度为O(n)。相比较而言&#xff0c;使用数组会方便很多&#xff0c;实现O(1)访问。 \qquad 所以这个题&#xff0c;可以先遍历一遍把数值存到数组中&#xff0c;再使用双指针判断是否是…

【Kubernetes】可视化UI界面Dashboard

安装和配置k8s可视化UI界面 一、安装Dashboard1.1、上传镜像并解压1.2、安装dashboard组件1.3、修改service1.4、访问dashboard 二、通过Token令牌访问Dashboard2.1、创建clusterrolebinding2.2、获取token2.3、使用token登录 三、通过kubeconfig文件访问Dashboard3.1、创建clu…

音视频技术开发周刊 | 322

每周一期&#xff0c;纵览音视频技术领域的干货。 新闻投稿&#xff1a;contributelivevideostack.com。 超级AI不会主宰人类&#xff0c;但人工智能必须开源&#xff01;LeCun最新采访引全网300万人围观 LeCun最新访谈视频中&#xff0c;再次坦露了自己对开源AI的看法。超级AI…