智能指针(1)
智能指针(1)
- 概念
- 内存泄漏
- 指针指针概念
- RAII
- 使用裸指针存在的问题
- 智能指针使用
- 分类
- unique(唯一性智能指针)
- 介绍
- 智能指针的仿写
- 代码理解
- 删除器
概念
内存泄漏
内存泄漏:程序中已动态分配的堆内存由于某些原因而未释放或无法释放,导致程序运行速度减慢甚至崩溃。
内存泄漏分为两类:
- 堆内存泄漏:在堆区申请了资源,结束时候没有将其堆区进行释放,导致该内存无法再次被使用。(或者说指向堆区内存的指针丢失)
- 资源泄漏:通常指系统资源,比如套接字,文件描述符等,因为这些在系统中都是有限制的,如果创建了而不归还,长时间就会耗尽资源,导致其他程序不可用。(例如:创建文件文件描述符(打开文件),但不归还(不关闭文件))。
指针指针概念
在java和C#语言中都有自动的垃圾回收机制,系统可以自动释放资源,为此在这些语言中,内存管理都不是问题。
C++中没有 垃圾回收机制,必须自动取释放资源,否则就会出现内存泄漏,因为在开发中内存泄漏很容易出现,但是又必须耗费大量精力去寻找解决内存泄漏,所以出现了智能指针。
int main() {int* ip=new int(10);if(ip!=nullptr) {//.....}//因为代码处理复杂,忘记释放return 0;
}
如上面就会出现内存泄漏,而解决内存泄漏最有效的方法就是指针指针。
智能指针:智能指针就是一个对象,智能指针对象中有一个指针,该指针存储的动态动态船舰对象的地址,用于生存期的控制,能够确保指针指针在对象离开作用域是自动调用析构函数来销毁对象(释放内存),防止内存泄漏。使用了智能指针就不会担心内存泄漏的问题了,也不需要手动释放内存了。
RAII
RAII是资源获取即初始化,使局部对象来管理资源的技术称为资源获取即初始化。
改方法就是充分利用了C++局部对象自动销毁的特性来控制资源的生命周期。
这些资源主要指操作系统中内存,套接字等,局部对象是指存储在栈中的对象,其生命周期靠操作系统来管理,无需人工介入。
RAII过程四个步骤:
- 设计一个类封装资源。
- 在构造函数中初始化。
- 在析构函数中执行销毁操作。
- 使用时定义一个类的局部对象。
class Object {int* ip;
public:Object(int val = 0) {ip = new int(val);cout << "create obj" << endl;}~Object() {delete ip;cout << "destory obj" << endl;}
};
void func() {Object obj(3);return;
}
int main() {func();return 0;
}
在func函数中创建了obj对象,obj也就是局部对象,在函数结束时,系统会自动释放销毁对象,也就自动调用了obj对象的析构函数。为此我们在使用资源的时候,在构造函数中进行初始化,在析构函数中进行销毁。
使用裸指针存在的问题
- 难以区分指针指向的是单一对象还是一组对象
- 使用完无法判断是否应该销毁对象,因为无法判断是否拥有指向的对象。(销毁之后仍然指向堆内存,但是堆内存已经释放)
- 在已经确定了销毁指针的情况下,也无法确定是用那个关键字来删除,例如delete,fopen等
- 即使确定了销毁指针的方法,也无法判断删除的对象是一组还是一个(delete,delete[])
- 上面问题就算都解决了,也难以保证在代码所有执行路径中有且只有一次销毁指针。
- 理论上没有办法来分辨一个指针是否处于悬挂状态。
智能指针使用
分类
C++11提供了4钟智能指针:auto_ptr,unique_ptr,shared_ptr和weak_ptr.
使用时需要引入头文件:#include < memory >
因为auto_ptr时C98标准中的,C98中没有右值引用,也没有move和forward,为此已经被C++11所弃用,所有此处不做过多介绍。
智能指针主要应用于多线程问题,因为在多线程中自主控制释放极为复杂,智能指针可以在线程结束时自动释放该对象的资源。
unique(唯一性智能指针)
介绍
我们以自定义的int类型指针为例:
int main() {unique_ptr<PtrInt> pa(new PtrInt(10));pa->Print();pa->SetValue(200);pa->Print();return 0;
}
我们创建了唯一性指针对象pa,指向船舰的PtrInt对象,可以通过指针来调用类的成员方法。并且会自动析构。
错误使用
my_unique_ptr<PtrInt> pa3(new PtrInt(3));//A
int main() {my_unique_ptr<PtrInt> pa(new PtrInt(10));PtrInt p(1);my_unique_ptr<PtrInt> pa1(&p);//BPtrInt* ip = new PtrInt(2);my_unique_ptr<PtrInt> pa2(ip);//C
}
A:使用智能指针管理全局变量是没有意义的,因为程序结束后,指针才销毁,都不如直接定义一个全局变量,其会在程序结束时自动销毁。(智能指针主要依赖局部对象来释放)
B:对象本身就是在栈中,而智能指针是处理堆区堆存,所以是错误的。
C:该方法本质上不是错误的,因为你用了裸指针,如果发现用了指针还没释放,就会释放两次,导致程序崩溃。
而这个唯一性指针是怎么实现的呢?
智能指针的仿写
template<class _Tp>
struct my_default_deleter
{void operator()(_Tp* ptr)const {delete ptr;}
};
template<class _Tp>
struct my_default_deleter<_Tp[]>
{void operator()(_Tp* ptr)const {delete []ptr;}
};
struct FileDeleter {void operator()(FILE* pf) const {fclose(pf);}};
template<class _Tp,class _Dp=my_default_deleter<_Tp>>
//template<class _Tp,class _Dp>
class my_unique_ptr//<_Tp[],_Dp>
{
public:using pointer = _Tp*;using elemtype_type = _Tp;using deleter_type = _Dp;
private:_Tp* ptr;deleter_type mDeleter;
public:/*deleter_type get_deleter()const {return mDeleter;}*/my_unique_ptr(_Tp* p = nullptr) :ptr(p) { cout << "create myuniqueptr" << endl; }//构造函数~my_unique_ptr() {//析构mDeleter(ptr);//mDeleter.operator()(ptr);//get_deleter()(ptr);ptr = nullptr;cout << "deatory myuniqueptr" << endl;}my_unique_ptr(const my_unique_ptr&) = delete;//删除拷贝构造my_unique_ptr& operator=(const my_unique_ptr&) = delete;//删除左值赋值重载my_unique_ptr(my_unique_ptr&& right) :ptr(right.ptr) {//移动构造right.ptr = nullptr;cout << "move create myuniqueptr" << endl;}my_unique_ptr& operator=(my_unique_ptr&& right) {//右值赋值重载if (this == &right) return *this;reset(right.release());//首先返回参数对象的资源指针,然后用该指针指向的资源代替现有指针的资源return *this;}pointer get() const { return ptr; }//返回指针_Tp& operator*()const { return *get(); }//指针解引用pointer operator->()const { return get(); }//指向符号重载pointer release() {//资源返回,将指向资源的指针返回pointer p = ptr;ptr = nullptr;return p;}void reset(pointer p = nullptr) {//资源移动,将形参指针指向的资源覆盖掉原本的资源,释放原本资源。mDeleter(ptr);ptr = p;}void swap(my_unique_ptr& u) {//交换指针指向的资源std::swap(this->ptr, u.ptr);}operator bool() const {//判断是否指向有效空间return get() != nullptr?true:false;}
};
上面就是指针指针的仿写,下面是使用
指向符重载,解引用:可以看出智能指针在这一块的使用和裸指针相同
int main() {my_unique_ptr<PtrInt> pa(new PtrInt(10));(*pa).SetValue(100);(*pa).Print();pa->SetValue(200);pa->Print();//pa.operator->()->Print();此处系统省去了一个指向符。return 0;
}
reset函数和release函数
release():返回指针
reset():资源覆盖,释放原本资源
int main() {my_unique_ptr<PtrInt> pa(new PtrInt(10));pa->Print();pa.reset(new PtrInt(100));//用新资源覆盖旧资源pa->Print();
}
int main() {my_unique_ptr<PtrInt> pa(new PtrInt(10));PtrInt* p = pa.get();//获取了指针,delete p;//errorPtrInt* ip = pa.release();//直接将指针指向nullptr,并且将指向资源的指针返回。
}
swap()函数:智能交换具名对象
int main() {my_unique_ptr<PtrInt> pa(new PtrInt(10));my_unique_ptr<PtrInt> pb(new PtrInt(20));pa->Print();pb->Print();pa.swap(pb);//只能和具名对象进行交换pa->Print();pb->Print();return 0;
}
判空:
判断指针是否为nullptr
int main() {unique_ptr<PtrInt> pa(new PtrInt(10));unique_ptr<PtrInt> pb(new PtrInt(20));my_unique_ptr<PtrInt> pc;if (pc) {//判断是否指向有效空间pc->Print();}
}
移动拷贝,移动赋值,拷贝构造,赋值重载
因为赋值重载和拷贝构造都是默认的浅拷贝,所以会出现两个指针指向同一资源,导致释放两次出现错误,所以呢删除了系统默认的拷贝构造函数和赋值重载。
而移动构造和移动赋值是可以实现的。这也就是唯一性智能指针的特点,有且只能管理一份资源。
int main() {my_unique_ptr<PtrInt> pa(new PtrInt(10));//my_unique_ptr<PtrInt> pb(pa);//浅拷贝 errormy_unique_ptr<PtrInt> pb(new PtrInt(20));//pa = pb;//浅拷贝 errmy_unique_ptr<PtrInt> pc(std::move(pa));pa = std::move(pb);return 0;
}
代码理解
my_unique_ptr<PtrInt> func(int x) {my_unique_ptr<PtrInt> pa(new PtrInt(x));return pa;
}
int main() {my_unique_ptr<PtrInt> pa(new PtrInt(10));pa = func(100);return 0;
}
首先在主函数中创建了只能指针对象,然后在func函数中船舰了智能指针的另一个对象,函数结束时使用移动构造来构建一个将亡值对象,然后移动赋值给pa对象。

void func(my_unique_ptr<PtrInt> px) {
}
int main() {my_unique_ptr<PtrInt> pa(new PtrInt(10));func(std::move(pa));pa->Print();return 0;
}
该程序会崩掉,因为把pa的资源移动到了px参数中,不存在拷贝构造,所以程序会因为pa指向为nullptr而崩掉。
void func(my_unique_ptr<PtrInt>& px) {px->Print();px->SetValue(200);px->Print();px.release();//my_unique_ptr<PtrInt> pb(std::move(px));
}
int main() {my_unique_ptr<PtrInt> pa(new PtrInt(10));func(pa);pa->Print();//errorreturn 0;
}
不能用移动语义接收智能指针,可以通过引用来接收智能指针,但不要在函数内部让让智能指针的资源移动走,否则智能指针指向nullptr。唯一性指针智能控制一个对象的资源。
删除器
在上面代码仿写中仿写了删除器,主要是为了使程序可用性更高,用到了仿函数,大家可以看一看以前的文章理解一下。
int main() {my_unique_ptr<FILE,FileDeleter> pfile(fopen("D:/tulun/ConsoleApplication6/ConsoleApplication6.cpp", "w"));//D:\tulun\ConsoleApplication6\ConsoleApplication6.cppreturn 0;
}
结果如下:

相关文章:
智能指针(1)
智能指针(1) 概念内存泄漏指针指针概念RAII使用裸指针存在的问题 智能指针使用分类unique(唯一性智能指针)介绍智能指针的仿写代码理解删除器 概念 内存泄漏 内存泄漏:程序中已动态分配的堆内存由于某些原因而未释放…...
Steemit 会颠覆 Quora/知乎 甚至 Facebook 吗?
Steemit是基于区块链技术的社交媒体平台,其独特的激励机制吸引了众多用户。然而,是否能够真正颠覆Quora、知乎甚至Facebook这些已经成为社交巨头的平台,仍然存在着许多未知因素。本文将探讨Steemit的优势和挑战,以及其在社交领域中…...
002Mybatis初始化引入
引入依赖 <dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId> </dependency> 自动检测工程中的DataSource创建并注册SqlSessionFactory实例创建并注册SqlSessionTemplate实例自…...
系统架构师之高内聚低耦合
一、概念: 标记耦合(Stamp Coupling)和数据耦合(Data Coupling)是软件设计中两种不同的耦合类型,它们之间的区别如下: 标记耦合:标记耦合是指模块之间通过参数传递标记或标识符来进…...
Netty核心源码剖析(二)
1.Netty接受请求过程源码剖析 1>.从之前的Netty启动过程源码剖析中,我们得知服务器最终注册了一个Accept事件等待客户端的连接.我们也知道,NioServerSocketChannel将自己注册到了bossGroup单例线程池(reactor线程)上,也就是EventLoop; 2>.先简单说下EventLoop的逻辑,Ev…...
「C/C++」C/C++ Lamada表达式
✨博客主页:何曾参静谧的博客 📌文章专栏:「C/C」C/C程序设计 相关术语 Lambda表达式:是C11引入的一种函数对象,可以方便地创建匿名函数。与传统的函数不同,Lambda表达式可以在定义时直接嵌入代码ÿ…...
bug(Tomcat):StandardContext.startInternal 由于之前的错误,Context[/day01]启动失败
引出 项目启动失败,一个困扰了一上午的bug 报错信息 org.apache.catalina.core.StandardContext.startInternal 一个或多个筛选器启动失败。完整的详细信息将在相应的容器日志文件中找到 org.apache.catalina.core.StandardContext.startInternal 由于之前的错误…...
Java性能权威指南-总结6
Java性能权威指南-总结6 垃圾收集入门垃圾收集概述GC算法选择GC算法 垃圾收集入门 垃圾收集概述 GC算法 JVM提供了以下四种不同的垃圾收集算法: Serial垃圾收集器 Serial垃圾收集器是四种垃圾收集器中最简单的一种。如果应用运行在Client型虚拟机(Windows平台上的32位JVM或…...
群的定义及性质
群的定义 设 < G , ⋅ > \left<G,\cdot\right> ⟨G,⋅⟩为独异点,若 G G G中每个元素关于 ⋅ \cdot ⋅都是可逆的,则称 < G , ⋅ > \left<G,\cdot\right> ⟨G,⋅⟩为群 由于群中结合律成立,每个元素的逆元是唯一的 …...
mac电脑git clone项目时报错证书过期和权限被拒绝
mac电脑使用git clone命令克隆项目时,一开始一直提示证书过期 SSL certificate problem: certificate has expired 执行以下代码关掉验证后,解决了这个问题 找到git目录 Git\git-cmd输入命令跳转到bin目录,cd bin输入命令运行git.exe执行关…...
【AIGC】Photoshop AI Beta版本安装使用(永久免费)
AIGC 大爆发 Adobe近日宣布,Photoshop(测试版)应用程序发布了生成式AI绘图,这是世界上第一个创意和设计工作流程的副驾驶,为用户提供了一种神奇的新工作方式。生成式AI绘图由Adobe Firefly提供支持,Adobe的…...
01 云原生生态系统解读
云计算的技术革命 互联网时代的历程 云计算到底是什么 云计算历程 云平台的优缺点 优势 稳定性:云平台大量资源,分布式集群部署,保障服务永不宕机,几个9弹性扩展:按需索取,一键秒级开通需要的资源安全性&…...
Java——Java易错选择题复习(2)(计算机网络)
1. 下面关于源端口地址和目标端口地址的描述中,正确的是( ) A. 在TCP/UDP传输段中,源端口地址和目的端口地址是不能相同的 B. 在TCP/UDP传输段中,源端口地址和目的端口地址必须是相同的 C. 在TCP/UDP传输段中ÿ…...
【HTML5系列教程】
《HTML5系列教程》目录大纲: 介绍 内容包括HTML简介、服务器的概念、B/S、C/S软件架构、前端与后端的开发内容、HTML发展历程、浏览器内核介绍、Web标准、WebStorm工具的使用、WebStorm常用快捷键、HTML常用标签 如:文本标签(span)、排版标签(div/p/h…...
二、电压源、电流源、受控源
点我回到目录 目录 理想电压源 理想电流源 受控源 电流源做功问题 电压源做功问题 理想电压源 •定义:两端电压保持定值或一定的时间函数,且电压值与流过它的电流i无关 •特点:理想电压源两端的电压由本身决定,与外电路无关…...
骨传导是哪个意思,推荐几款性能优的骨传导耳机
骨传导耳机是通过头部骨迷路传递声音,而不是直接通过耳膜的振动来传递声音。与传统的入耳式耳机相比,骨传导耳机不会堵耳朵,在跑步、骑车等运动时可以更好的接收外界环境音,保护听力,提升安全性。此外,骨…...
利用Taro打造灵活的移动App架构
最近公司的一些项目需要跨端框架,技术老大选了Taro,实践了一段时间下来,愈发觉得Taro是个好东西,所以在本篇文章中稍微介绍下。 什么是Taro? Taro(或称为Taro框架)是一种用于构建跨平台应用程…...
(转载)基于模拟退火算法的TSP问题求解(matlab实现)
1 理论基础 1.1 模拟退火算法基本原理 模拟退火(simulated annealing,SA)算法的思想最早是由Metropolis等提出的。其出发点是基于物理中固体物质的退火过程与一般的组合优化问题之间的相似性。模拟退火法是一种通用的优化算法,其物理退火过程由以下三部分组成&am…...
splitpcap 使用说明
背景 当PCAP原始文件特别巨大的时候,整个文件直接载入内存是相当耗时的,于是一个简单的想法是将大的PCAP切分成若干小PCAP。对于这个任务,现有工具splitcap是可以完成的。无论是按照主机对、还是按照五元组信息切分,splitcap都会…...
配置docker阿里云镜像加速
默认情况下docker安装镜像文件是从docker官方的镜像中心下载:https://hub.docker.com/ , 有时速度慢,可以通过配置docker阿里云镜像来加速,配置后,就从国内阿里云下载。 注册阿里云用户,登录->工作台-&g…...
深入剖析AI大模型:大模型时代的 Prompt 工程全解析
今天聊的内容,我认为是AI开发里面非常重要的内容。它在AI开发里无处不在,当你对 AI 助手说 "用李白的风格写一首关于人工智能的诗",或者让翻译模型 "将这段合同翻译成商务日语" 时,输入的这句话就是 Prompt。…...
CVPR 2025 MIMO: 支持视觉指代和像素grounding 的医学视觉语言模型
CVPR 2025 | MIMO:支持视觉指代和像素对齐的医学视觉语言模型 论文信息 标题:MIMO: A medical vision language model with visual referring multimodal input and pixel grounding multimodal output作者:Yanyuan Chen, Dexuan Xu, Yu Hu…...
基于ASP.NET+ SQL Server实现(Web)医院信息管理系统
医院信息管理系统 1. 课程设计内容 在 visual studio 2017 平台上,开发一个“医院信息管理系统”Web 程序。 2. 课程设计目的 综合运用 c#.net 知识,在 vs 2017 平台上,进行 ASP.NET 应用程序和简易网站的开发;初步熟悉开发一…...
解决Ubuntu22.04 VMware失败的问题 ubuntu入门之二十八
现象1 打开VMware失败 Ubuntu升级之后打开VMware上报需要安装vmmon和vmnet,点击确认后如下提示 最终上报fail 解决方法 内核升级导致,需要在新内核下重新下载编译安装 查看版本 $ vmware -v VMware Workstation 17.5.1 build-23298084$ lsb_release…...
oracle与MySQL数据库之间数据同步的技术要点
Oracle与MySQL数据库之间的数据同步是一个涉及多个技术要点的复杂任务。由于Oracle和MySQL的架构差异,它们的数据同步要求既要保持数据的准确性和一致性,又要处理好性能问题。以下是一些主要的技术要点: 数据结构差异 数据类型差异ÿ…...
C++八股 —— 单例模式
文章目录 1. 基本概念2. 设计要点3. 实现方式4. 详解懒汉模式 1. 基本概念 线程安全(Thread Safety) 线程安全是指在多线程环境下,某个函数、类或代码片段能够被多个线程同时调用时,仍能保证数据的一致性和逻辑的正确性…...
鸿蒙DevEco Studio HarmonyOS 5跑酷小游戏实现指南
1. 项目概述 本跑酷小游戏基于鸿蒙HarmonyOS 5开发,使用DevEco Studio作为开发工具,采用Java语言实现,包含角色控制、障碍物生成和分数计算系统。 2. 项目结构 /src/main/java/com/example/runner/├── MainAbilitySlice.java // 主界…...
使用Spring AI和MCP协议构建图片搜索服务
目录 使用Spring AI和MCP协议构建图片搜索服务 引言 技术栈概览 项目架构设计 架构图 服务端开发 1. 创建Spring Boot项目 2. 实现图片搜索工具 3. 配置传输模式 Stdio模式(本地调用) SSE模式(远程调用) 4. 注册工具提…...
RabbitMQ入门4.1.0版本(基于java、SpringBoot操作)
RabbitMQ 一、RabbitMQ概述 RabbitMQ RabbitMQ最初由LShift和CohesiveFT于2007年开发,后来由Pivotal Software Inc.(现为VMware子公司)接管。RabbitMQ 是一个开源的消息代理和队列服务器,用 Erlang 语言编写。广泛应用于各种分布…...
【Android】Android 开发 ADB 常用指令
查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...
