C++:智能指针

C++在用引用取缔掉指针的同时,模板的引入带给了指针新的发挥空间
智能指针简单的来说就是带有不同特性和内存管理的指针模板

  • unique_ptr
    1.不能有多个对象指向一块内存
    2.对象释放时内部指针指向地址也随之释放
    3.对象内数据只能通过接口更改绑定
    4.对象只能接收右值或者将亡值
  • shared_ptr
    1.可以有多个指针对象指向一块地址
    2.使用一块堆区空间维护一块地址被指向的次数
    3.当指向一个地址的指针数量变为0时清除这块空间
  • weak_ptr
    和shared_ptr搭配使用解决了循环引用问题

首先unique_ptr实现起来其实很简单,只需要对赋值函数(=运算符重载)和拷贝构造函数等这些利用对象创建新的对象的函数做出修改即可,当然析构函数部分的释放也是要注意的。

template<typename T>
class unique_ptr{T*ptr;
public:unique_ptr(T*p=nullptr):ptr(p){}unique_ptr(unique_ptr&&other) noexcept:ptr(other.release()){other.ptr=nullptr;}unique_ptr& operator=(unique_ptr&&other) noexcept{if(other.ptr!=this->ptr){change(other.release());}return *this;}T*get()const{return ptr;}T& operator*() const{return *ptr;}T* operator->() const{return ptr;}explicit operator bool() const{return ptr!=nullptr;}~unique_ptr(){delete ptr;ptr=nullptr;}T*release(){T*tmp=this->ptr;this->ptr=nullptr;return tmp;}void change(T*cur=nullptr){if(cur!=ptr){delete ptr;ptr=cur;}}unique_ptr(const unique_ptr&)=delete;unique_ptr& operator=(const unique_ptr&)=delete;
};

然后看shared_ptr它的话要比unique_ptr要自由的多实现起来也很简单,只需要多一步计数操作即可。

template<typename T>
class shared_ptr{T*ptr;int* cnt;
public:shared_ptr(T*p=nullptr):ptr(p),cnt(new int(1)){}shared_ptr(const shared_ptr&other) noexcept:ptr(other.ptr),cnt(other.cnt){(*cnt)++;}shared_ptr& operator=(const shared_ptr&other) noexcept{if(this!=&other){release();this->ptr=other.ptr;this->cnt=other.cnt;(*cnt)++;}return *this;}T*get()const{return ptr;}T& operator*() const{return *ptr;}T* operator->() const{return ptr;}explicit operator bool() const{return ptr!=nullptr;}~unique_ptr(){release();}void release(){if(cnt&&--(*cnt)==0){delete cnt;delete ptr;}ptr=nullptr;cnt=nullptr;}
};

但是注意shared_ptr的计数可能会带来一个问题:循环引用
看下面代码其实很好理解,就是循环指向造成计数到不了0从而释放不了对象。

class B;
class A{
public:shared_ptr<B>aptr;
};
class B{
public:shared_ptr<A>bptr;
};
int main(){shared_ptr<A>a=new A;shared_ptr<B>b=new B;a->aptr=b;b->bptr=a;
}

解决方式:引入了weak_ptr搭配shared_ptr使用,weak_ptr是对对象的一种弱引用,它不会增加对象的use_count,weak_ptr和shared_ptr可以相互转化,shared_ptr可以直接赋值给weak_ptr,weak_ptr也可以通过调用lock函数来获得shared_ptr。

  1. weak_ptr指针通常不单独使用,只能和 shared_ptr 类型指针搭配使用。将一个weak_ptr绑定到一个shared_ptr不会改变shared_ptr的引用计数。一旦最后一个指向对象的shared_ptr被销毁,对象就会被释放。即使有weak_ptr指向对象,对象也还是会被释放。
  2. weak_ptr并没有重载operator->和operator *操作符,因此不可直接通过weak_ptr使用对象,典型的用法是调用其lock函数来获得shared_ptr示例,进而访问原始对象。
  3. 需要注意weak_ptr不维护资源的释放,因为需要避免资源被释放两次,因此weak_ptr常用在为生命周期一致的shared_ptr赋值
template <typename T>
class MySharedPtr;template <typename T>
class MyWeakPtr;template <typename T>
class MySharedPtr {
private:T* ptr;int* refCount;int* weakCount;public:explicit MySharedPtr(T* p = nullptr) : ptr(p), refCount(new int(1)), weakCount(new int(0)) {}MySharedPtr(const MySharedPtr& other) : ptr(other.ptr), refCount(other.refCount), weakCount(other.weakCount) {(*refCount)++;}~MySharedPtr() {release();}int use_count() const {return (refCount ? *refCount : 0);}T* get() const {return ptr;}T& operator*() const {return *ptr;}T* operator->() const {return ptr;}MyWeakPtr<T> weak_ptr() {(*weakCount)++;return MyWeakPtr<T>(this->ptr, this->refCount, this->weakCount);}MySharedPtr& operator=(const MySharedPtr& other) {if (this != &other) {release();ptr = other.ptr;refCount = other.refCount;weakCount = other.weakCount;(*refCount)++;}return *this;}private:void release() {if (refCount) {(*refCount)--;if (*refCount == 0) {delete ptr;delete refCount;if (*weakCount == 0) {delete weakCount;}}}}friend class MyWeakPtr<T>;
};template <typename T>
class MyWeakPtr {
private:T* ptr;int* refCount;int* weakCount;public:MyWeakPtr() : ptr(nullptr), refCount(nullptr), weakCount(nullptr) {}MyWeakPtr(T* p, int* rc, int* wc) : ptr(p), refCount(rc), weakCount(wc) {}MyWeakPtr(const MyWeakPtr& other) : ptr(other.ptr), refCount(other.refCount), weakCount(other.weakCount) {(*weakCount)++;}~MyWeakPtr() {release();}MySharedPtr<T> lock() {if (expired()) {return MySharedPtr<T>();}(*refCount)++;return MySharedPtr<T>(ptr, refCount, weakCount);}MyWeakPtr& operator=(const MyWeakPtr& other) {if (this != &other) {release();ptr = other.ptr;refCount = other.refCount;weakCount = other.weakCount;(*weakCount)++;}return *this;}bool expired() const {return (refCount == nullptr || *refCount == 0);}
private:void release() {if (weakCount) {(*weakCount)--;if (*weakCount == 0) {delete weakCount;}}}
};

shared_ptr和weak_ptr可以互相构造,看下面的代码就知道它们是如何解决循环引用的情况

class B;
class A{
public:MYShared_Ptr<B>aptr;
};
class B{
public:MYWeak_Ptr<A>bptr;
};
int main(){MYShared_Ptr<A>a=new A;MYShared_Ptr<B>b=new B;a->aptr=b.lock();b->bptr=a;
}

简单的来说就是通过两个可以互相转换的类型避免产生同种类型的环,对不同类型的环它们释放的时候互不影响对方的计数所以可以正常释放。

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

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

相关文章

20240202在Ubuntu20.04.6下使用whisper.cpp的CPU模式

20240202在Ubuntu20.04.6下使用whisper.cpp的CPU模式 2024/2/2 14:15 rootrootrootroot-X99-Turbo:~/whisper.cpp$ ./main -l zh -osrt -m models/ggml-medium.bin chs.wav 在纯CPU模式下&#xff0c;使用medium中等模型&#xff0c;7分钟的中文视频需要851829.69 ms&#xf…

常见的图形化编程工具都有什么

图形化编程是一种通过可视化界面和模块化组件来进行程序设计的方法&#xff0c;它使得编程更加直观和易于理解&#xff0c;尤其适合初学者和儿童学习编程。在这篇文章中&#xff0c;6547网题库将介绍图形化编程的基本概念、优势以及一些常见的图形化编程工具。 一、图形化编程的…

CAD-autolisp(三)——文件、对话框

目录 一、文件操作1.1 写文件1.2 读文件 二、对话框DCL2.1 初识对话框2.2 常用对话框界面2.2.1 复选框、列表框2.2.2 下拉框2.2.3 文字输入框、单选点框 2.3 Lisp对dcl的驱动2.4 对话框按钮实现拾取2.5 对话框加载图片2.5.1 幻灯片图片制作2.5.1 代码部分 一、文件操作 1.1 写…

密钥加密问题

C参考代码&#xff1a; #include<iostream> #include<map> #include<vector> using namespace std; int main() {vector<char> x;vector<char> y;map<char,char> word;char ch getchar();getchar();string str;getline(cin,str);for(cha…

HashMap的几种遍历方式

entry 增强for循环方式 Map<Integer, String> map new HashMap<>(); map.put(1, "a"); map.put(2, "b"); map.put(3, "c"); map.put(4, "d");Set<Map.Entry<Integer, String>> entrySet map.entrySet();fo…

(科目一)阅读理解

1、题型介绍 1、材料分析题&#xff1a;第三题14分。 2、文章600~1300字左右&#xff0c;篇幅较之前有所增加。 3、题量&#xff1a; 第1问概括&#xff1a;4分。&#xff08;理解文章中的重要概念或句子&#xff09;第2问分析&#xff1a;10分。&#xff08;分析文章中心论…

GmSSL - GmSSL的编译、安装和命令行基本指令

文章目录 Pre下载源代码(zip)编译与安装SM4加密解密SM3摘要SM2签名及验签SM2加密及解密生成SM2根证书rootcakey.pem及CA证书cakey.pem使用CA证书签发签名证书和加密证书将签名证书和ca证书合并为服务端证书certs.pem&#xff0c;并验证查看证书内容&#xff1a; Pre Java - 一…

JDK版本如何在IDEA中切换

JDK版本在IDEA中切换 一、项目结构设置 1.Platform——Settings 项目结构---SDKS 2.Project——SDK 3.Modules——SDK——Sources 4.Modules——SDK——Dependencies 二、设置--编译--字节码版本 Settings——Build,——Java Compiler

【Servlet】——Servlet API 详解

个人主页&#xff1a;兜里有颗棉花糖 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 兜里有颗棉花糖 原创 收录于专栏【Servlet】 本专栏旨在分享学习Servlet的一点学习心得&#xff0c;欢迎大家在评论区交流讨论&#x1f48c; 目录 一、HttpServlet二、Htt…

spring boot yaml文件中如何设置duration对象值

Spring Boot对表示持续时间有专门的支持。如果您公开java.time.Duration属性&#xff0c;则应用程序对应Duration类型的属性有以下格式可用: long类型的常规表示(使用毫秒作为默认单位&#xff0c;除非指定了DurationUnit)java.time.Duration 使用的标准ISO-8601格式其中值和单…

鸿蒙ArkUI日期选择组件

鸿蒙ArkUI日期选择组件&#xff0c;基于基础组件进行的二次封装的日期选择组件&#xff0c;快速实现日期选择。 /*** 日期*/ Component export default struct DiygwDate{//绑定的值Link Watch(onValue) value:string;// 隐藏值State valueField: string value;// 显示值Sta…

6+单基因+单细胞+实验,干湿结合是生信分析发文最真诚的必杀技

今天给同学们分享一篇生信文章“CXCR4 expression is associated with proneural-to-mesenchymal transition in glioblastoma”&#xff0c;这篇文章发表在Int J Cancer期刊上&#xff0c;影响因子为6.4。 结果解读&#xff1a; CXCR4表达与PN GBM的存活和MES标记物的表达相关…

数与抽象之负数和分数

负数和分数 “探索减法和除法&#xff1a;从具体操作到方程求解的数学思维” 谁如果给小孩子教过数学&#xff0c;那一定会知道&#xff0c;减法和除法并不像加法和乘法那样直接&#xff0c;他们学习起来更困难一些。为了解释减法&#xff0c;我们当然可以利用“拿走”的概念…

Echarts+Vue 首页大屏静态示例Demo 第三版

效果图: 源码: <template><div class="content bg" style="height: 100vh;overflow-y: auto" :class="{ fullscreen-container: isFullScreen }"><div class="reaDiv" style="height: 10vh"><div…

latex multirow学习

今天搞了一晚上的这个multirow&#xff0c;总算弄出来了几个比较好的例子&#xff0c;主要是这个multirow的语法我没看懂&#xff0c;这个逻辑我是没理解&#xff0c;就很尴尬&#xff0c;一改就报错&#xff0c;只能先弄几个例子&#xff0c;自己慢慢试

MySQL 5.7.36安装操作

接上文提示&#xff1a; 【Config Type】选项用于设置服务器的类型。单击该选项右侧的下三角按钮&#xff0c;即可看到 3个选项&#xff0c;如图10所示。 Development Machine&#xff08;开发者机器&#xff09;&#xff1a;代表典型的个人桌面工作站。假定机器上运行着多个…

4.java中的输入输出/输入中的next和nextLine区别问题

&#xff08;笔试会经常让我们自己去处理输入输出&#xff09; 一.输出到控制台 println 输出的内容自带 \n&#xff08;换行&#xff09; print 不带 \n printf 的格式化输出方式和 C 语言的 printf 是基本一致的. String msg "Hello, World!";System.out.print(m…

Transformer 自然语言处理(四)

原文&#xff1a;Natural Language Processing with Transformers 译者&#xff1a;飞龙 协议&#xff1a;CC BY-NC-SA 4.0 第十章&#xff1a;从头开始训练变换器 在本书的开头段落中&#xff0c;我们提到了一个名为 GitHub Copilot 的复杂应用&#xff0c;它使用类似 GPT 的…

前缀和 差分

差分和前缀和都是算法里边比较重要的知识点&#xff0c;不过学习的难度并不高&#xff0c;这篇文章会讲解相关的内容。 1. 前缀和怎么玩 1&#xff09;一维前缀和 在该数之前&#xff0c;包括该数的所有数之和&#xff0c;有点类似高中学的数列的前n项和Sn。 2&#xff09;二维…

Spring框架——主流框架

文章目录 Spring(轻量级容器框架)Spring 学习的核心内容-一图胜千言IOC 控制反转 的开发模式Spring快速入门Spring容器剖析手动开发- 简单的 Spring 基于 XML 配置的程序课堂练习 Spring 管理 Bean-IOCSpring 配置/管理 bean 介绍Bean 管理包括两方面: Bean 配置方式基于 xml 文…