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

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();//不一块内存,递减countthis->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.mzph.cn/news/200181.shtml

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

相关文章

视觉测量基础

1. 相机模型 1.1 坐标系转换原理 世界坐标系(world Coords):点在真实世界中的位置&#xff0c;描述相机位置。 相机坐标系(Cameras Coords):以相机光学系统中心&#xff08;镜头中心&#xff09;为原点&#xff0c;建立相机坐标系。 图像物理坐标系(Film Coords):经过小孔成…

21 俄罗斯套娃信封问题

问题描述&#xff0c;给你一个二维数组envelopes&#xff0c;其中envelopes[i][wi,hi].表示第i个信封的宽度和高度&#xff0c;当另一个信封的宽度和噶度都比这个信封大的时候就可以放进另一个信封里&#xff0c;如同俄罗斯套娃一样。请计算最多能有多少个信封能组成一套“俄罗…

Debugging Application Engine Programs 调试应用程序引擎程序

Debugging Application Engine Programs 调试应用程序引擎程序 This section discusses how to: 本节讨论如何&#xff1a; Enable the Application Engine debugger.启用应用程序引擎调试器。Set debugging options.设置调试选项。 Enabling the Application Engine Debu…

Java数组面试题

Java数组面试题 1. 创建一个包含多个元素的数组&#xff0c;并打印输出数组的内容。 public class Main {public static void main(String[] args) {String[] array {"apple", "banana", "orange"};for (String element : array) {System.out…

微服务实战系列之J2Cache

前言 经过近几天陆续发布Cache系列博文&#xff0c;博主已对业界主流的缓存工具进行了基本介绍&#xff0c;当然也提到了一些基本技巧。相信各位盆友看见这么多Cache工具后&#xff0c;在选型上一定存在某些偏爱: A同学说&#xff1a;不管业务千变万化&#xff0c;我对Redis的…

【华为OD题库-070】数列描述-java

题目 题目描述: 有一个数列A[n]&#xff0c;从A[0]开始每一项都是一个数字&#xff0c;数列中A[n1]都是A[n]的描述&#xff0c;其中A[0]1&#xff0c;规则如下; A[0]:1 A[1]:11 含义其中A[0]1是1个1即11&#xff0c;表示A[0]从左到右连续出现了1次1 A[2]:21 含义其中A[1]11是2个…

企业如何制定精准营销策略?

在当今的数字化时代&#xff0c;位置数据已经成为企业营销策略中不可或缺的一部分。通过收集和分析客户的位置数据&#xff0c;企业可以更好地了解客户的行为和需求&#xff0c;制定更精准的营销策略&#xff0c;从而提高营销效率。 首先&#xff0c;利用IP地址位置数据可以帮助…

手搓图片滑动验证码_JavaScript进阶

手搓图片滑动验证码 背景代码效果图展示网站 背景 在做前端项目开发的时候&#xff0c;少不了登录注册部分&#xff0c;既然有登录注册就少不了机器人验证&#xff0c;验证的方法有很多种&#xff0c;比如短信验证码、邮箱验证码、图片滑动、图片验证码等。 由于鄙人在开发中…

9个Logo素材超多的Logo网站!

Logo 虽然看起来很简单&#xff0c;但是设计过程中的每一个细节都很精致。因为 Logo 作为品牌的象征&#xff0c;应该一目了然地传达给人们品牌的理念和形象。本文给大家整理了 7 个 Logo 素材网站和 2 个 Logo 在线制作网站。可以收集很多关 Logo 设计的内容和技巧&#xff01…

吉他初学者学习网站搭建系列(5)——如何做一个在线节拍器

文章目录 背景实现TransportLoop代码 在线尝试 背景 我们看吉他谱时&#xff0c;经常看到拍号&#xff0c;例如6/8。它的含义是一拍是一个八分音符&#xff0c;一小节有六拍。四分音符的时长是一秒&#xff0c;即60拍/分钟。基于这样的背景知识&#xff0c;我们就可以根据一些…

决策树 C4.5算法

C4.5算法 C4.5算法 C4.5 算法是 Ross 对ID3 算法的改进用信息增益率来选择属性。ID3选择属性用的是子树的信息增益而C4.5用的是信息增益率在决策树构造过程中进行剪枝对非离散数据也能处理能够对不完整数据进行处理 信息增益比&#xff08;C4.5&#xff09; g R ( D , A ) …

Leetcode 第 110 场双周赛 Problem D 2809. 使数组和小于等于 x 的最少时间(DP+贪心+正难则反)

Leetcode 第 110 场双周赛 Problem D 2809. 使数组和小于等于 x 的最少时间&#xff08;DP 好题&#xff09;题目 给你两个长度相等下标从 0 开始的整数数组 nums1 和 nums2 。每一秒&#xff0c;对于所有下标 0 < i < nums1.length &#xff0c;nums1[i] 的值都增加 num…

supervisor管理python进程

前言 平时开发调试中使用conda环境&#xff0c;项目比较多环境多&#xff0c;而且命令繁杂&#xff0c;每一次启动项目都可能会因为忘记启动方式而频繁报错。现在可以通过supervisor来管理&#xff0c;只需要配置几个文件&#xff0c;就可以轻松通过简单一致的命令启动工程&…

C++ day55 判断子序列 不同的子序列

题目1&#xff1a;392 判断子序列 题目链接&#xff1a;判断子序列 对题目的理解 判断字符串s是否为t的子序列 字符串s和字符串t的长度大于等于0&#xff0c;字符串s的长度小于等于字符串t的长度&#xff0c;本题其实和最长公共子序列的那道题很相似&#xff0c;相当于找两…

HashMap相关专题

前置知识&#xff1a;异或运算 异或运算介绍 异或有什么神奇之处&#xff08;应用&#xff09;&#xff1f; &#xff08;1&#xff09;快速比较两个值 &#xff08;2&#xff09;我们可以使用异或来使某些特定的位翻转&#xff0c;因为不管是0或者是1与1做异或将得到原值的相…

IntelliJ IDEA 2023.2新特性详解第三弹!Docker、Kubernetes等支持!

9 Docker 在 Docker 镜像层内预览文件 现在可以在 Services&#xff08;服务&#xff09;工具窗口中轻松访问和预览 Docker 镜像层的内容。 从列表选择镜像&#xff0c;选择 Show layers&#xff08;显示层&#xff09;&#xff0c;然后点击 Analyze image for more informati…

ES6对象

1.对象简写 ES6中规定可以直接在对象中写入变量和函数作为对象的属性和方法&#xff0c;此时属性名为变量名, 属性值为变量的值。对象简写在未来的使用频率极其高。 let namelarry;let age12;let obj{name,age,//es5 sayName:function(){}// sayName(){// console.log(t…

<软考>软件设计师-2操作系统(总结)

(一) 进程管理 1 操作系统概述 1-1 操作系统定义: 能有效地组织和管理系统中的各种软/硬件资源&#xff0c;合理地组织计算机系统工作流程&#xff0c;控制程序的执行&#xff0c;并且向用户提供一个良好的工作环境和友好的接口。 1-2 操作系统的作用: 1 通过资源管理提高计…

7+WGCNA+机器学习+实验+泛癌分析,多要素干湿结合

今天给同学们分享一篇生信文章“Analysis and Experimental Validation of Rheumatoid Arthritis Innate Immunity Gene CYFIP2 and Pan-Cancer”&#xff0c;这篇文章发表在Front Immunol期刊上&#xff0c;影响因子为7.3。 结果解读&#xff1a; DEG筛选和数据预处理 数据在…

Helplook VS Google Docs:一对一比较

还记得Google Docs在2006年一炮走红的时候吗&#xff1f;它很大程度地改变了协作方式&#xff0c;也减少了附加文件和频繁保存的麻烦。相比Microsoft Word&#xff0c;很多人更喜欢Google Docs的简单性。 但是时代也在不断地发展。像HelpLook这样的新竞争对手也可以提供先进的…