C++智能指针(二十)

一.RAII(Resource Acquisition Is Initialization

RAII资源获取即初始化,RAII的思想就是在构造时初始化资源,或者托管已经构造的资源。在析构的时候释放资源。一般不允许复制或赋值,并且提供若干的资源访问的方法。比如:我们创建一个文件读写的类,当我们通过类创建一个栈对象时,并初始化传入要打开文件的指针,当我栈对象的生命周期结束会自动调用析构函数将文件关闭,这就为我们解决一些文件未close的安全问题。

#include <iostream>
#include <string>
#include <string.h>
using std::endl;
using std::cout;
using std::string;class safeFile
{
private:FILE* _fp;
public:safeFile(FILE* fp):_fp(fp){cout<<"safeFile(FILE* fp)"<<endl;}//一般不允许赋值和复制     删除或者设置成私有的safeFile(const safeFile& rhs)=delete;safeFile& operator=(const safeFile& rhs)=delete;void write(const string& msg){fwrite(msg.c_str(),1,strlen(msg.c_str()),_fp);cout<<"void write(const string& msg)"<<endl;}~safeFile(){if(_fp){fclose(_fp);cout<<"~safeFile()"<<endl;}}
};int main()
{safeFile sf(fopen("test.txt","a+"));//sf 是栈对象销毁时自动执行关文件的操作sf.write(string("hello,world"));}

我们在简单的实现一下RAII

#include <iostream>using std::cout;
using std::endl;template<typename T>
class RAII
{
private:
T* _data;public:RAII(T* data):_data(data){cout<<"RAII(T* data)"<<endl;}~RAII(){cout<<"~RAII()"<<endl;if(_data){delete _data;_data=nullptr;}}T* operator->(){return _data; }T& operator*(){return *_data;}T* get(){return _data;}
private:RAII(const RAII& rhs);RAII& operator=(const RAII& rhs);
};class Point
{
private:int _a;int _b;
public:Point(int a=0,int b=0):_a(a),_b(b){cout<<"Point(int a=0,int b=0)"<<endl;}~Point(){cout<<"~Point()"<<endl;}void print(){cout<<"( "<<_a<<" , "<<_b<<" )"<<endl;}
};int main()
{//pt本身不是指针,但是具备指针的功能RAII<Point> pt(new Point(1,2));pt.operator->()->print();pt->print();
}

总结:这里pt本身不是指针,但他具备指针的功能,我们是用pt对象来托管new Point(1,2)这块堆空间,当pt对象的生命周期结束,自动调用析构函数,我们在将这块托管的堆空间释放。并且我们在RAII内设置一些方法可以访问这块托管的堆空间资源,这样我们就不用担心内存泄漏的问题,防止堆空间资源用完没有进行回收。

二.智能指针

在C++动态内存管理中,我们通过new在动态内存中为对象分配空间并返回一个指向该对象的指针,我们可以选择对对象进行初始化,通过delete接受一个动态对象的指针将该对象销毁。动态内存的使用很容易出问题,因为要确保在合适的时间释放内存是非常困难的。有时候我们会忘记释放内存,这时就会产生内存泄漏;有时在还有指针指向内存时我们就将内存释放了,这种情况会导致引用非法指针。因此为了更加容易和安全使用动态内存,标准库提供了智能指针类型来管理动态对象。

头文件<memory>

1.shared_ptr

shared_ptr既可以传左值也可以传右值,其中我们可以认为每个shared_ptr都有一个引用计数。无论何时我们拷贝一个shared_ptr引用计数都会递增。例如,当用一个shared_ptr去初始化另一个shared_ptr,或者将他作为参数传递给一个函数以及作为参数的返回值时,她所关联的计数器就会递增。当我们给shared_ptr赋予一个新值或是shared_ptr被销毁(例如一个局部的shared_ptr离开其作用域)时计数器就会递减,如果引用计数减少为0时,他就会自动释放自己所管理的类。

#include <iostream>
#include <memory>using std::cout;
using std::endl;
using std::shared_ptr;
using std::string;
using std::make_shared;
using std::weak_ptr;//与unique_ptr相反的是shared_ptr既可以传左值也可以传右值
void test()
{string str("helloworld");// shared_ptr<string> sp=std::make_shared<string>(new char[str.size()]());// *sp=str;//通过make_shared返回shared_ptr类型的指针,来对sp进行初始化shared_ptr<string> sp=std::make_shared<string>(str);cout<<"*sp = "<<*sp<<endl;cout<<"-------------------------------------"<<endl;shared_ptr<int> sp1(new int(10));cout<<"&*sp1 = "<<&*sp1<<endl;cout<<"*sp1 = "<<*sp1<<endl;cout<<"sp1.get() = "<<sp1.get()<<endl;cout<<"sp1.use_count() = "<<sp1.use_count()<<endl;cout<<"sp1.unique() = "<<sp1.unique()<<endl;cout<<endl;cout<<"赋值运算符函数"<<endl;shared_ptr<int> sp2(new int(20));cout<<"*sp2 = "<<*sp2<<endl;cout<<"sp2.get() = "<<sp2.get()<<endl;sp2=sp1;cout<<"&*sp2 = "<<&*sp2<<endl;cout<<"*sp2 = "<<*sp2<<endl;cout<<"sp2.get() = "<<sp2.get()<<endl;cout<<"sp2.use_count() = "<<sp2.use_count()<<endl;cout<<"sp2.unique() = "<<sp2.unique()<<endl;cout<<endl;shared_ptr<int> sp3=sp2;cout<<"&*sp3 = "<<&*sp3<<endl;cout<<"*sp3 = "<<*sp3<<endl;cout<<"sp3.get() = "<<sp3.get()<<endl;cout<<"sp3.use_count() = "<<sp3.use_count()<<endl;cout<<"sp3.unique() = "<<sp3.unique()<<endl;}int main()
{test();return 0;
}

2.unique_ptr

与shared_ptr不同的是,某个时刻只能有一个unique_ptr指向一个给定的对象。当unique_ptr被销毁时,它所指向的对象也被销毁。并且unique_ptr也没有类似make_shared的标准库函数返回一个unique_ptr。当我们定一个unique_ptr时,需要将其绑定到一个new返回的指针上。类似shared_ptr,初始化unique_ptr必须采用直接初始化方式。因为unique_ptr没有拷贝构造函数和赋值运算符函数,但是具有移动语义(有移动构造函数和移动赋值函数)并且可以作为容器元素,但是只能传递右值。

#include <iostream>
#include <memory>
#include <vector>using std::vector;
using std::unique_ptr;
using std::cout;
using std::endl;void test()
{unique_ptr<int> up(new int(2));// unique_ptr<int> up1=up;//error 没有拷贝构造函数
// unique_ptr<int> up2;
// up2=up; //error没有赋值运算符vector<unique_ptr<int>> vec_up;
//vec_up.push_back(up);   //error 还是会调用拷贝构造函数  unique_ptr(const unique_ptr&) = delete;vec_up.push_back(std::move(up));
vec_up.push_back(unique_ptr<int>(new int(29)));cout<<"vec_up[0] = "<<*vec_up[0]<<endl<<"vec_up[1] = "<<*vec_up[1]<<endl;
}int main()
{test();
}

左右值相互转换的方法:

 需要右值的时候
 将左值转换为右值std::move()或构建临时对象Point(1,2)
 需要左值的时候
 可以使用构造函数创建对象,创建有名对象。Point pt(1,2)
 Point&& rhf=Point(1,2)   利用右值引用是左值的特性

3.weak_ptr

shared_ptr有一个缺陷就是循环引用的情况例如:

#include <iostream>
#include <memory>using std::cout;
using std::endl;
using std::shared_ptr;
using std::string;
using std::make_shared;
using std::weak_ptr;class Child;class Parent
{
public:Parent(){cout<<"Parent()"<<endl;}~Parent(){cout<<"~Parent()"<<endl;}shared_ptr<Child> pchild;
};class Child
{
public:Child(){cout<<"Child()"<<endl;}~Child(){cout<<"~Child()"<<endl;}shared_ptr<Parent> pparent;
};
//shared_ptr的问题循环引用的问题
void circle_test()
{shared_ptr<Parent> spParent(new Parent());shared_ptr<Child> spChild(new Child());cout<<"spParent.use_count() = "<<spParent.use_count()<<endl;cout<<"spChild.use_count() = "<<spChild.use_count()<<endl;//循环引用会导致内存泄露。解决方法weak_ptr和shared_ptr配合使用;其中weak_ptr不会使引用计数++spParent->pchild=spChild;   //sp=spspChild->pparent=spParent;     cout<<"spParent.use_count() = "<<spParent.use_count()<<endl;cout<<"spChild.use_count() = "<<spChild.use_count()<<endl;
}
int main()
{circle_test();
}

可以看出来此时只有构造函数没有析构函数这就会造成内存泄漏。

创建完对象时指针引用如下图所示:

当进行析构时

就会造成引用计数不为零,并且指针相互只向。

class Child;class Parent
{
public:Parent(){cout<<"Parent()"<<endl;}~Parent(){cout<<"~Parent()"<<endl;}weak_ptr<Child> pchild;
};class Child
{
public:Child(){cout<<"Child()"<<endl;}~Child(){cout<<"~Child()"<<endl;}weak_ptr<Parent> pparent;
};

当将类的数据成员变成weak_ptr时

因为weak_ptr不会使引用计数递增

此时释放时引用计数都为零可以将堆空间进行释放。

weak_ptr的一些注意事项:

  1. weak_ptr没有成员访问运算符->和解引用运算符*。因此不能用weak_ptr指针去访问托管的数据。
  2. weak_ptr的初始化
    1. weak_ptr<Point> wp创建空对象
    2. weak_ptr<Point> wp(sp)用shared_ptr的指针类型对weak_ptr进行初始化
    3. weak_ptr<Point> wp=sp1
  3. 将weak_ptr提升一个shared_ptr,因为weak_ptr不能查看管理资源内容,转化为shared_ptr shared_ptr<Point> sp4=wp.lock();
  4. bool flag=wp.expired();如果use_count为0则flag为true,否则use_count不为0则flag为false 

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

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

相关文章

URL地址解析至页面展示全过程(面试详细解答)

目录 1、解析URL 2、缓存判断 ​编辑3、DNS解析 ​编辑4、获取MAC地址 5、TCP三次握手 6、HTTP请求 7、服务器处理请求&#xff0c;返回HTTP响应 8、页面渲染 9、TCP四次挥手 10、浏览器解析HTML 11、浏览器布局渲染 1、解析URL 首先会对 URL 进行解析&#xff0c;…

RS232、RS485、RS422、TTL、CAN各自的区别

目录 一&#xff1a;工业串口通信标准RS232、RS485、RS422的区别 第一个区别、硬件管脚接口定义不同 第二个区别、工作方式不同 第三个区别、通信方式不同 第四个区别&#xff0c;逻辑特性不同 第五个区别、抗干扰性、传输距离和传输速率也不同 二&#xff1a;RS232、RS…

docker安装并跑通QQ机器人实践(4)-bs-cqhttp搭建

go-cqhttp&#xff0c;基于 Mirai 以及 MiraiGo 的 OneBot Golang 原生实现&#xff0c;只需简单的配置, 就可以基于 go-cqhttp 使用框架开发&#xff0c;具有轻量, 原生, 高并发, 低占用, 跨平台等特点。 1 go-cqhttp 官网及可执行文件下载链接 go-cqhttp 官网&#xff1a;ht…

【Linux】详解进程通信中信号量的本质同步和互斥的概念临界资源和临界区的概念

一、同步和互斥的概念 1.1、同步 访问资源在安全的前提下&#xff0c;具有一定的顺序性&#xff0c;就叫做同步。在多道程序系统中&#xff0c;由于资源有限&#xff0c;进程或线程之间可能产生冲突。同步机制就是为了解决这些冲突&#xff0c;保证进程或线程之间能够按照既定…

泛型的初步认识(2)

前言~&#x1f973;&#x1f389;&#x1f389;&#x1f389; hellohello~&#xff0c;大家好&#x1f495;&#x1f495;&#xff0c;这里是E绵绵呀✋✋ &#xff0c;如果觉得这篇文章还不错的话还请点赞❤️❤️收藏&#x1f49e; &#x1f49e; 关注&#x1f4a5;&#x…

优思学院|ISO45001职业健康安全管理体系是什么?

ISO45001:2018是新公布的国际标准规范&#xff0c;全球备受期待的职业健康与安全国际标准&#xff08;OH&S&#xff09;于2018年公布&#xff0c;并将在全球范围内改变工作场所实践。ISO45001将取代OHSAS18001&#xff0c;成为全球工作场所健康与安全的参考。 ISO45001:201…

微信域名防封/QQ域名防封/域名状态检测/域名防红防封API平台源码

下载地址&#xff1a;API平台源码 这套源码是使用thinkphp3.1.3开发的&#xff0c;可以在PHP5.3-5.6下运行&#xff0c;程序是有一点老了&#xff0c;但是思路仍在&#xff01;然后&#xff0c;这套源码我已经成功搭建起来了&#xff0c;后台、个人&#xff08;用户&#xff0…

在瑞芯微RV1126 Linux系统上调试WiFi的详细指南

目录标题 1. **系统和环境准备**2. **检查WiFi设备状态**3. **启用和禁用WiFi接口**4. **扫描可用的WiFi网络**5. **连接到WiFi网络**6. **查看当前的WiFi连接状态**7. **断开和重新连接WiFi**8. **管理WiFi网络配置**9. **使用iw工具进行高级WiFi调试**10. **故障排除和日志获…

算法训练营day16

一、二叉树的最大深度 递归解法 后序遍历(DFS) class Solution {public int maxDepth(TreeNode root) {if (root null) return 0;return Math.max(maxDepth(root.left), maxDepth(root.right)) 1;} }算法解析&#xff1a; 终止条件&#xff1a; 当 root 为空&#xff0c;…

力扣---填充每个节点的下一个右侧节点指针 II

给定一个二叉树&#xff1a; struct Node {int val;Node *left;Node *right;Node *next; } 填充它的每个 next 指针&#xff0c;让这个指针指向其下一个右侧节点。如果找不到下一个右侧节点&#xff0c;则将 next 指针设置为 NULL 。 初始状态下&#xff0c;所有 next 指针都…

牛x之路 - Day1

Day1 微积分之屠龙宝刀&#xff08;武林秘籍&#xff09; 之前的一些东西都在pdf上记得笔记&#xff0c; 没有在这个上面展示一遍&#xff0c;只好学到相关内容的时候再提叙啦&#xff1b;所以其实再写这个小记的时候&#xff0c;我已经看了一半的书&#xff0c;但是不要紧&am…

IntelliJ IDEA运行发布传统Java Web Application项目

接 重温8年前项目部署 要求&#xff0c;如何改用IntelliJ IDEA运行发布传统 Java Web Application项目呢&#xff0c;简述步骤如下&#xff1a; 一、下载源码 源码&#xff1a;https://github.com/wysheng/kindergarten 下载后的本地项目路径&#xff1a;/Users/songjianyon…

《Python源码剖析》之对象的基石---PyObject

前言 在python的源代码中&#xff0c;PyObject的结构体定义如下&#xff0c;它的内容看起来很简单&#xff0c;只有3项&#xff0c;分别是&#xff1a;_PyObject_HEAD_EXTRA&#xff0c;ob_refcnt和ob_type&#xff0c;其中_PyObject_HEAD_EXTRA是用于指向活动堆的指针&#x…

学习大数据,所需要的linux基础(1)

文章目录 linux入门概述Linux和Windows的区别CentOS下载地址 Linux文件与目录结构Linux文件Linux目录结构 VI/VIM编辑器vi/vim是什么测试数据集准备一般模式编辑模式指令模式模式间转换 网络配置和系统管理操作查看网络IP和网关配置网络和ip地址ifconfig配置网络接口修改ip地址…

使用Python进行自动化测试

&#x1f47d;发现宝藏 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。【点击进入巨牛的人工智能学习网站】。 如何使用Python进行自动化测试&#xff1a;测试框架的选择与应用 自动化测试是软件开发过程…

curlftpfs和fusermount

curlftpfs 是一种 Linux 系统下用来将 FTP 服务器挂载为文件系统的工具&#xff0c;这意味着可以通过本地目录来访问和操作 FTP 服务器上的文件。 挂载FTP服务器到本地系统 为了挂载FTP服务器到本地系统中&#xff0c;使用curlftpfs工具&#xff0c;可以按照以下格式书写命令…

保姆级教程!QRCNN-BiLSTM一键实现多变量回归区间预测!区间预测全家桶再更新!

​ 声明&#xff1a;文章是从本人公众号中复制而来&#xff0c;因此&#xff0c;想最新最快了解各类智能优化算法及其改进的朋友&#xff0c;可关注我的公众号&#xff1a;强盛机器学习&#xff0c;不定期会有很多免费代码分享~ 今天对我们之前推出的区间预测全家桶进行…

进程间通信IPC(二)

一、存储映射I/O(Memory-mapped I/O) 使一个磁盘文件与存储空间中的一个缓冲区相映射。于是从缓冲区中取数据&#xff0c;就相当于读文件中的相应字节。与此类似&#xff0c;将数据存入缓冲区&#xff0c;则相应的字节就自动写入文件。这样&#xff0c;就可在不使用read和write…

由于找不到msvcp110d.dll,无法继续执行代码

在计算机软件开发和运行环境中&#xff0c;动态链接库&#xff08;DLL&#xff09;文件扮演着至关重要的角色。它们封装了特定功能的代码&#xff0c;使得多个应用程序能够共享这些功能而无需重复编译或加载相同的代码&#xff0c;从而显著提升了系统资源利用率和软件开发效率。…

024——驱动、server、client、GUI全功能联调

目录 一、本次修改 二、GUI和Client之间联调 2.1 工程结构修改 2.2 将TCP程序修改为可被其它程序调用 2.3 优化显示界面 2.4 解决GUI通过tcp send的问题 2.5 处理服务器数据 时间不是很多了&#xff0c;我想压缩一下快点把属于毕设的这部分搞完&#xff0c;俺要出去旅游…