C++11 智能指针

文章目录

  • 什么是智能指针
  • 为什么用智能指针
  • 智能指针的类型和各自的功能
    • auto_ptr(c98使用的,现已废弃)
    • unique_ptr
    • shared_ptr
    • weak_ptr
  • unique_ptr和shared_ptr的简单模拟实现

什么是智能指针

智能指针是一种在编程中用于管理动态分配内存的指针。其使用了资源获取即初始化(RAII)"的模式。它是一种封装了原始指针的对象,提供了额外的功能,例如:自动内存管理、所有权传递、线程安全等等。智能指针通过一定的规则来确保动态分配内存的正确使用,避免内存泄漏、重复释放、使用已经释放的内存等问题。C++ 语言中常见的智能指针包括 std::unique_ptr、std::shared_ptr、std::weak_ptr 等。

为什么用智能指针

这时大家就会有个疑问了?既然已经有了指针,我们程序员自己就可以对内存资源的释放,为什么还要智能指针呢,这不是多此一举吗?
其实不然,有些情况简单对内存释放是并不能达到我们预期的结果的,举个栗子:

int *p = new int[10];
... // 功能操作
delete[] p;

乍一看,好像上述的代码没有什么问题,申请了内存,也手动释放了内存。但是这真的就是不会出问题吗?

并不是!如果在功能操作里面发生了异常,使代码的执行顺序跳过了delete[]释放的语句,这样就会导致内存泄漏。类似的例子是在函数体里return的返回,这都会导致程序并没有按照我们所愿的执行顺序执行下去,从而导致内存泄漏和线程安全。

所以使用智能指针就可以很大程度的减轻我们对内存资源的管理压力。

  • 优点
  1. 内存泄漏:当使用动态分配内存时,如果忘记释放内存,就会导致内存泄漏。而智能指针可以在对象被销毁时自动释放它所管理的内存,避免了手动释放内存的问题。
  2. 多线程安全:当多个线程同时访问同一个对象时,如果没有正确地使用同步机制,就会导致内存管理问题。而智能指针可以提供线程安全的内存访问,避免了多线程竞争的问题。

智能指针的类型和各自的功能

既然智能指针如此重要,我们现在就来学习一下他们的类型和作用。

智能指针

auto_ptr(c98使用的,现已废弃)

  • auto_ptr运用了RAII思想,能够实现对资源的自动释放。
  • 拥有与指针一样的使用功能

使用例子:

class Animal
{
public:~Animal() { cout << "~Animal()" << endl; }
};class Dog: public Animal
{
public:~Dog() { cout << "~Dog()" << endl; }
};
void ptr()
{auto_ptr<Dog> p(new Dog);
}

缺点:

  1. 尽可能不要将auto_ptr变量定义为全局变量或指针
  2. 除非自己知道后果,不要把auto_ptr智能指针赋值给同类型的另外一个智能指针
  3. auto_ptr不支持容器。
  4. auto_ptr的析构函数调用的是delete不是delete [],所以不能使用智能指针数组。

判断一个auto_ptr是否为空不能使用if(ptest == NULL),应该使用if(ptest.get() == NULL)

auto_ptr的拷贝赋值就是资源所有权的转移,会出现意想不到的意外
在这里插入图片描述

unique_ptr

前面说了,auto_ptr是存在隐患的,所以就c++11就出来了个unique_ptr,这个智能指针是安全的,并且具备auto_ptr的所有功能。

至于为什么安全呢?这里解释一下,因为他把拷贝构造函数,赋值运算符函数都禁止使用了
在这里插入图片描述
看看源码
在这里插入图片描述

shared_ptr

unique_ptr是很安全了,但是美中不足的是他不可以进行赋值,我们都知道,原始指针是可以进行赋值,所以unique_ptr还是不够完美的,此时shared_ptr就闪亮登场了。

shared_ptr指针不仅安全,而且还可以进行赋值。

那么他是如何做到赋值的功能的呢?下面就是它的实现原理

引用计数”,是一种内存管理技术,用于跟踪内存中对象的引用数量。每当有一个指针引用一个对象时,引用计数就会增加;相应地,当指针不再引用对象时,引用计数就会减少。当引用对象没有任何活动引用,可以安全地释放其占用的内存空间。

是的,shared_ptr里面有个计数器
在这里插入图片描述
shared_ptr看似十分完美,但是却暗藏杀机

是的,存在循环引用的绝杀

当使用shared_ptr存在循环引用时,对象的内存可能无法被正确释放,导致内存泄漏。以下是一个示例代码,展示了shared_ptr中循环引用的缺陷:

#include <memory>
class A {
public:std::shared_ptr<A> B;~A() {std::cout << "Destructor called." << std::endl;}
};int main() {std::shared_ptr<A> a1(new A());std::shared_ptr<A> a2(new A());a1->B = a2;a2->B = a1;return 0;
}

在上面的代码中,我们创建了两个A的对象,a1和a2。然后,我们将它们相互引用,形成了循环引用关系。a1拥有指向a2的智能指针,而a2也拥有指向a1的智能指针。

由于循环引用,当程序结束时,这两个对象的析构函数不会被调用,对象所占用的内存也无法被正确释放,造成了内存泄漏。通过输出语句std::cout << "Destructor called." << std::endl;可以观察到析构函数没有被用。

为避免该问题,可以使用weak_ptr来打破循环引用,或者手动解除循环引用关系,例如通过调用reset()将智能指针重置为nullptr。

weak_ptr

是弱引用,是资源的观察者,依托于shared_ptr得以发挥作用,主要用于检查资源是否存在,不影响资源的生命周期

template<class T>
shared_ptr<T> s = new int(10);
weak_ptr<T> w(s);w.reset();     // 将w置空
w.use_count(); // 返回与w共享对象的shared_ptr的数量
w.expired();   // w.use_count()为0,则返回true,否则返回false
w.lock();      // w.expired()为真,返回一个指向w的shared_ptr对象;否则返回空shared_ptr

回到shared_ptr的循环引用的问题,weak_ptr的主要作用也就是解决循环引用问题

#include <memory>
class A {
public:// std::shared_ptr<A> B;std::weak_ptr<A> B;  // 换成弱引用即可解决问题~A() {std::cout << "Destructor called." << std::endl;}
};int main() {std::shared_ptr<A> a1(new A());std::shared_ptr<A> a2(new A());a1->B = a2;a2->B = a1;return 0;
}

unique_ptr和shared_ptr的简单模拟实现

直接上代码

  • unique_ptr
template<class T>
class unique_ptr
{
public:unique_ptr() : ptr_(nullptr) {}unique_ptr(T* up) : ptr_(up) {}unique_ptr(const unique_ptr<T>&) = delete;unique_ptr<T>& operator=(const unique_ptr<T>&) = delete;T& operator*() { return *ptr_; }T* operator->() { return ptr_; }~unique_ptr(){if(ptr_)delete ptr_;}private:T* ptr_;
};
  • shared_ptr
template<class T>
class shared_ptr
{
public:shared_ptr(): ptr_(nullptr), count_(nullptr){}shared_ptr(T* sp): ptr_(sp), count(new int(1)) {}shared_ptr(const shared_ptr<T>& sp) : ptr_(sp), count_(sp.count_) { (*count_)++; }T& operator*() { return *ptr_; }T* operator->() { return ptr_; }T* get() { return ptr_; }shared_ptr<T>& operator=(const shared_ptr<T>& sp){if (sp.ptr_ != ptr_){// 释放原有空间,防止内存泄漏release();_ptr = sp._ptr;_count = sp._count;(*_count)++;}return *this;}~shared_ptr(){release();}
private:void release(){if (-- * count == 0 && ptr_){delete count_;delete ptr_;count_ = ptr_ = nullptr;}}
private:T* ptr_;int* count_;
};

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

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

相关文章

DC电源模块如何调节电源输出电压和电流

BOSHIDA DC电源模块如何调节电源输出电压和电流 DC电源模块是一种电源转换器&#xff0c;在电子设备中广泛使用。它可以将交流电转换为直流电&#xff0c;或者将低电压直流电转换为高电压直流电。 DC电源模块通常可以调节输出电压和电流&#xff0c;以满足各种电子设备的不同需…

【C++】 使用红黑树模拟实现STL中的map与set

文章目录 前言1. 对之前实现的红黑树进行一些补充和完善1.1 析构1.2 查找 2. STL源码中map和set的实现3. 改造红黑树封装map和set3.1 红黑树结构修改3.2 map、set的结构定义3.3 insert的封装3.4 insert测试3.5 发现问题并解决3.6 红黑树迭代器实现3.7 封装set和map的迭代器并测…

RK3399平台开发系列讲解(内核调试篇)Valgrind使用案例

🚀返回专栏总目录 文章目录 一、使用未初始化的内存案例二、内存泄露三、在内存被释放后进行读/写案例四、从已分配内存块的尾部进行读/写案例五、两次释放内存案例沉淀、分享、成长,让自己和他人都能有所收获!😄 📢Valgrind 是一个开源的内存调试和性能分析工具,用于…

Electron 报gpu_process_host.cc(951)] GPU process launch faile错误

解决方法&#xff0c;在入口js文件中&#xff0c;添加如下代码: app.commandLine.appendSwitch(no-sandbox)

Eltima USB Network Gate 10.0 Crack

USB Network Gate -通过网络共享USB 设备 USB Network Gate (前身为以太网USB控制器USB) 轻松的通过网络(Internet/LAN/WAN)分享您的一个或者多个连接到您计算机的USB设备。 无论您身处异国还是近在隔壁办公室&#xff0c;您都可以轻松使用远程扫描仪、打印机、摄像头、调制解…

CVE-2015-5254漏洞复现

1.漏洞介绍。 Apache ActiveMQ 是美国阿帕奇&#xff08;Apache&#xff09;软件基金会所研发的一套开源的消息中间件&#xff0c;它支持 Java 消息服务&#xff0c;集群&#xff0c;Spring Framework 等。Apache ActiveMQ 5.13.0之前 5.x 版本中存在安全漏洞&#xff0c;该漏…

《HeadFirst设计模式(第二版)》第十一章代码——代理模式

代码文件目录&#xff1a; RMI&#xff1a; MyRemote package Chapter11_ProxyPattern.RMI;import java.rmi.Remote; import java.rmi.RemoteException;public interface MyRemote extends Remote {public String sayHello() throws RemoteException; }MyRemoteClient packa…

SpringBoot基于Zookeeper实现分布式锁

文章目录 问题背景前言实现搭建Zookeeper容器引入依赖ZK客户端的配置类ZK客户端的工厂类注入bean构建测试类 问题背景 研究分布式锁&#xff0c;基于ZK实现&#xff0c;需要整合到SpringBoot使用 前言 参考自SpringBoot集成Curator实现Zookeeper基本操作&#xff0c;Zookeeper入…

ssm+vue校园美食交流系统源码

ssmvue校园美食交流系统源码和论文026 开发工具&#xff1a;idea 数据库mysql5.7 数据库链接工具&#xff1a;navcat,小海豚等 技术&#xff1a;ssm 摘 要 随着现在网络的快速发展&#xff0c;网上管理系统也逐渐快速发展起来&#xff0c;网上管理模式很快融入到了许多商…

el-table 实现动态表头 静态内容 根据数据显示动态输入框

直接放代码了 <el-table:data"form.tableDataA"borderstripestyle"width: 100%; margin-top: 20px"><el-table-columnv-for"(category, categoryIndex) in form.tableDataA":key"categoryIndex":label"category.name&qu…

Java虚拟机(JVM):垃圾收集算法

目录 一、分代收集理论 二、标记-清除算法 三、标记-复制算法 四、标记-整理算法 一、分代收集理论 分代收集理论建立在两个分代假说之上&#xff1a; 1、弱分代假说&#xff1a;绝大多数对象都是朝生夕灭的。 2、强分代假说&#xff1a;熬过越多次垃圾收集过程的对象就…

5.8.webrtc事件处理基础知识

在之前的课程中呢&#xff0c;我向你介绍了大量web rtc线程相关内容&#xff0c;今天呢&#xff0c;我们来看一下线程事件处理的基本知识。首先&#xff0c;我们要清楚啊&#xff0c;不同的平台处理事件的API是不一样的&#xff0c;这就如同我们当时创建线程是类似的&#xff0…

K8s实战4-使用Helm在Azure上部署Ingress-Nginx和Tokengateway

手动发布Ingress-Nginx 1 登录到aks(dfinder-gw-aks) az login az account set --subscription ${sub ID} az aks get-credentials --resource-group ${groupname} --name ${aks name} 2 下载 ingress-nginx-4.2.5.tgz curl -LO https://github.com/kubernetes/ingress-ngi…

“开发和运维”只是一个开始,最终目标是构建高质量的软件工程

随着技术的飞速发展&#xff0c;软件行业不断寻求改进和创新的方法来提供更高质量的产品。在这方面&#xff0c;DevOps已经展现出了巨大的潜力。通过打破开发和运维之间的壁垒&#xff0c;DevOps将持续集成、持续交付和自动化流程引入到软件开发中&#xff0c;使团队能够更快地…

数字孪生助力智慧水务:科技创新赋能水资源保护

智慧水务中&#xff0c;数字孪生有着深远的作用&#xff0c;正引领着水资源管理和环境保护的创新变革。随着城市化和工业化的不断推进&#xff0c;水资源的可持续利用和管理愈发显得重要&#xff0c;而数字孪生技术为解决这一挑战提供了独特的解决方案。 数字孪生技术&#xf…

Docker容器无法启动 Cannot find /usr/local/tomcat/bin/setclasspath.sh

报错信息如下 解决办法 权限不够 加上--privileged 获取最大权限 docker run --privileged --name lenglianerqi -p 9266:8080 -v /opt/docker/lenglianerqi/webapps:/usr/local/tomcat/webapps/ -v /opt/docker/lenglianerqi/webapps/userfile:/usr/local/tomcat/webapps/u…

Qt安卓开发经验技巧总结V202308

01&#xff1a;01-05 pro中引入安卓拓展模块 QT androidextras 。pro中指定安卓打包目录 ANDROID_PACKAGE_SOURCE_DIR $$PWD/android 指定引入安卓特定目录比如程序图标、变量、颜色、java代码文件、jar库文件等。 AndroidManifest.xml 每个程序唯一的一个全局配置文件&…

【Redis】Redis中的布隆过滤器

【Redis】Redis中的布隆过滤器 前言 在实际开发中&#xff0c;会遇到很多要判断一个元素是否在某个集合中的业务场景&#xff0c;类似于垃圾邮件的识别&#xff0c;恶意IP地址的访问&#xff0c;缓存穿透等情况。类似于缓存穿透这种情况&#xff0c;有许多的解决方法&#xf…

基于MATLAB开发AUTOSAR软件应用层Code mapping专题-part 2 Inport和Outports 标签页介绍

上篇我们介绍了Function页的内容,这篇我们介绍Inports和Outports页的内容,这里我们再次强调一个概念,code mapping是以simulink的角度去看的,就是先要在模型中建立simulink模块,在code mapping里映射他要对应的autosar的元素,之后生成代码时的c语言的名字是以Autosar的元…

永久设置pip指定国内镜像源(windows内)

1.首先列出国内四个镜像源网站&#xff1a; 一、清华源 https://pypi.tuna.tsinghua.edu.cn/simple/ 二、阿里源 https://mirrors.aliyun.com/pypi/simple 三、中科大源 https://pypi.mirrors.ustc.edu.cn/simple/ 四、豆瓣源 http://pypi.douban.com/simple/ 2.一般下载所需要…