C++11 - 8 -智能指针

news/2024/4/30 1:13:05/文章来源:https://blog.csdn.net/buptsd/article/details/126879444

c++

C++11 - 智能指针

  • 前言:
  • 普通指针:
    • 安全隐患:
      • 其他函数异常:
      • new函数异常:
  • 智能指针:
    • RAII原理:
    • smart_ptr:
    • auto_ptr:
      • 管理权转移:
      • 优点:
      • 缺点:
    • unique_str:
      • 防拷贝:
      • 缺点:
    • shared_ptr:
      • 静态引用计数器:
        • 代码:
        • 漏洞:
          • 计数器混乱问题:
          • 直接指针构造问题:
      • 动态引用计数器:
        • 代码:
        • 优点:
        • 缺点:
          • 对象赋值拷贝时的计数器错误:
          • 多线程下的计数器安全问题:
      • 真·智能指针:
        • 代码:
        • 优点:
        • 缺点:
        • 漏洞:循环引用
    • weak_ptr:
      • 原理:
      • 使用:
    • 定制删除器:
      • 背景:
      • 删除器类型:
      • 模拟实现:
      • STL中的删除器:
        • 模板传入仿函数类:
        • 传参传入删除函数:

前言:

Vue框架:从项目学Vue
OJ算法系列:神机百炼 - 算法详解
Linux操作系统:风后奇门 - linux

普通指针:

安全隐患:

其他函数异常:

  • 查看下面这段存在异常的函数:
int div(){int a, b;cin >>a >>b;if(b == 0)throw invalid_argument("除0错误");return a/b;
}void func(){int *p = new int;cout<<div() <<endl;delete p;
}
int main(){try{func();}catch(const exception &e){cout<< e.what() <<endl;}return 0;
}
  • 异常来源:
    如果div()函数异常,由于不设置try catch机制,申请的p指针无法被delete释放
  • 处理策略:try catch
int div(){int a, b;cin >>a >>b;if(b == 0)throw invalid_argument("除0错误");return a/b;
}void func(){int *p = new int;try{cout<<div() <<endl;}catch(const exception &e){cout<< e.what() <<endl;}delete p;
}
int main(){try{func();}catch(const exception &e){cout<< e.what() <<endl;}return 0;
}

new函数异常:

  • 多次为指针申请空间,之后统一释放。

    当一次申请出错时,前面所有申请的资源无法释放:

int main(){int *p1 = new int;int *p2 = new int;int *p3 = new int;delete p1;delete p2;delete p3;return 0;
}
  • 处理策略:
    1. try + catch
    2. 设置初始值为nullptr,申请失败后值还为nullptr
int main(){int *p1 = nullptr;int *p2 = nullptr;int *p3 = nullptr;try{int *p1 = new int;int *p2 = new int;int *p3 = new int;}catch(...){if(p2 == nullptr)delete p1;if(p3 == nullptr){delete p1;delete p2;}}delete p1;delete p2;delete p3;return 0;
}

智能指针:

RAII原理:

  • RAII:resource acquisition is initialization

    1. 利用对象生命周期控制程序资源
    2. 在对象构造时获取资源
    3. 在对象析构时释放资源
    4. 实际上将一份资源的管理职责交给了一个对象
  • 好处:

    1. 不需要显式释放资源
    2. 对象所需资源在对象的生命周期内始终有效
  • 实质:

    一个成员变量是模板指针的类

    模板指针指向new函数的返回指针

    资源无效后析构时释放模板指针所指资源

smart_ptr:

  • 析构函数存在bug的版本:
template <class T>
class smart_ptr{private:T *ptr;public:smart_ptr(T *_ptr){ptr = _ptr;}T& operator*(){return *ptr;}T* operator->(){return ptr;}//有bug的析构函数~smart_ptr(){cout<<ptr <<" " <<*ptr <<endl;delete ptr;}
}
int main(){smart_ptr<int> sp1(new int);smart_ptr<int> sp2(new int);smart_ptr<int> sp3(new int);//析构函数bug体现:同一空间被析构两次,肯定报错int *p = new int;smart_ptr<int> sp4 = p;smart_ptr<int> sp5 = p;cout<<div() <<endl;return 0;
}
  • 很明显,这一版的SmartPtr不是存在缺点,而是存在析构漏洞,根本不能使用
  • 下面介绍C++98和C++11中几种防止SmartPtr()多次析构的不同设计

auto_ptr:

管理权转移:

  • 出现年代:c++98
  • 解决多次delete同一资源的方案:管理权转移
    每指向资源的指针多一个时
    1. 将对资源的管理权交给最新的指针
    2. 同时原来的指针都指向空
  • 代码:
template <class T>
class auto_ptr{private:T *ptr;public:auto_ptr(T *_ptr){ptr = _ptr;}auto_ptr(auto_ptr<T> &ap){ptr = ap.ptr;ap.ptr = nullptr;}auto_ptr<T>& operator=(auto_ptr<T> *ap){if(this != &ap){if(ptr)delete ptr;ptr = ap.ptr;ap.ptr = nullptr;}return *this;}T& operator*(){return *ptr;}T* operator->(){return ptr;}~auto_ptr(){delete ptr;}
};

优点:

  • 优点:

    直接了当的解决了双重析构的问题

缺点:

  • 缺点1:

    新的auto_ptr到来会覆盖旧的auto_ptr

    对于auto_ptr使用不熟悉的同学可能出这样的错

int main(){auto_ptr<int> sp1(new int);auto_ptr<int> sp2(sp1);*sp2 = 10;cout<< *sp2 <<endl;cout<< *sp1 <<endl;return 0;
}
  • 缺点2:

    管理权转移只存在于auto_ptr对象之间拷贝

    当存在两个auto_ptr对象都从一个指针初始化而来时,还是会出现双重析构问题:

int main(){int *p = new int(10);auto_ptr<int> ap1(p);auto_ptr<int> ap2(p);return 0;
}

unique_str:

  • 出现年代:boost库,同期三个智能指针为scoped_ptr / shared_ptr / weak_ptr
  • C++11对boost库的借鉴产物:unique_ptr / shared_ptr / weak_ptr
  • 解决多次delete同一资源的方案:防拷贝
    每个指针开辟空间之后,只能有一个unique_ptr/scoped_ptr对象封装该指针

防拷贝:

  • 防止拷贝函数:
    1. 法一:delete关键词
    2. 法二:私有 + 空实现(只声明不实现)
  • 代码:
template <class T>
class unique_ptr{private:T *ptr;//c++11中delete关键字屏蔽函数unique_ptr(unique_ptr<T> const &) = delete;unique_ptr& operator=(unique_ptr<T> const &) = delete;//c++98中私有 + 只声明不实现unique_ptr(unique_ptr<T> const &);unique_ptr& operator=(unique_ptr<T> const &);public:unique_ptr(T *_ptr){ptr = _ptr;}~unique_ptr(){delete ptr;}void Show(){cout<<*ptr <<endl;}
};

缺点:

  • 理论上每个指针都只能存在一个unique_ptr对象

  • 当不采用unique_ptr对象拷贝赋值,

    而是直接使用指针初始化两个unique_ptr对象时,还是存在双重析构

int *p = new int(10);
unique_ptr<int>  uq1(p);
unique_ptr<int>  uq2(p);

shared_ptr:

  • 原理:
    1. 记录有多少个对象管理着同一块资源
    2. 每个对象析构时计数器 - -
    3. 每个对象构造时计数器 ++
    4. 最后一个析构的对象负责释放资源

静态引用计数器:

代码:

  • 代码:
template <class T>
class shared_ptr{private:static int refCount;T *ptr;public:shared_ptr(T *_ptr){refCount = 0;ptr = _ptr;}shared_ptr(auto_ptr &ap){refCount++;ptr = ap.ptr;}~shared_ptr(){refCount--;if(refCount == 0 && ptr){delete ptr;}}
};
int main(){shared_ptr<int> sp1(new int);shared_ptr<int> sp2(sp1);shared_ptr<int> sp3(sp1);shared_ptr<int> sp4(new int);return 0;
}

漏洞:

计数器混乱问题:
  • 应该是每个资源独立使用一个自己的计数器

    如果所有资源都使用同一个引用计数器,那么会产生如下结果

  • 初始化时:

    1. sp1 借助 int* 初始化时,计数器refCount == 0
    2. sp2 借助 sp1 初始化时,计数器refCount == 1
    3. sp3 借助 sp1 初始化时,计数器refCount == 2
    4. sp4 借助 int* 初始化时,计数器refCount == 0
  • 析构时:

    sp1 & sp2 & sp3都对同一块内存析构,引发多重析构异常

直接指针构造问题:
  • 继续不按套路出牌,直接使用指针构造两个对象:
int *p = new int(10);
shared_ptr<int> sp1(p);		//静态计数器refCount == 0
shared_ptr<int> sp2(p);		//静态计数器refCount == 0
/*析构时直接双重析构,异常
*/
  • 静态引用计数器本身存储漏洞,且不能解决指针直接构造对象问题,

    下面来看看动态引用计数器能不能同时解决两个问题?

动态引用计数器:

代码:

  • 代码:
template <class T>
class shared_ptr{private:T *ptr;int *refCount;			//动态引用计数器public:shared_ptr(T *_ptr){ptr = _ptr;refCount = (new int(1));	//从这步开始已经决定了智能指针只能走对象拷贝路线,不能走指针直接构造路线}shared_ptr(const shared_ptr<T> &sp){ptr = sp.ptr;refCount = sp.refCount;(*refCount)++;}shared_ptr<T>& operator=(const shared_ptr<T> &sp){if(ptr != sp.ptr){ptr = sp.ptr;refCount = sp.refCount;(*refCount)++;}}~shared_ptr(){if(--(*refCount) == 0 && ptr){delete ptr;delete refCount;			}}
};
int main(){shared_ptr<int> sp1(new int);shared_ptr<int> sp2(sp1);shared_ptr<int> sp3(sp1);shared_ptr<int> sp4(new int);return 0;
}

优点:

  • 每个资源独立使用自己的计数器

    不同的资源计数器之间互不干扰

缺点:

对象赋值拷贝时的计数器错误:
  • 两个指针所指不同的对象,又发生赋值拷贝时:
class shared_ptr{shared_ptr<T>& operator=(const shared_ptr<T> &sp){if(ptr != sp.ptr){ptr = sp.ptr;refCount = sp.refCount;(*refCount)++;}}
};
  • 发生资源引用数只增不减,可能最终导致无法析构释放:
    引用数错误
  • 对策:每次对象拷贝构造时,先让原资源引用数–,再让现资源引用数++
class shared_ptr{shared_ptr<T>& operator=(const shared_ptr<T> &sp){if(ptr != sp.ptr){//原资源引用数--if(--(*refCount) == 0){delete ptr;delete refCount;}//先资源引用数++ptr = sp.ptr;refCount = sp.refCount;(*refCount)++;}}
};
多线程下的计数器安全问题:
  • 当多线程同时含有对同一块资源的智能指针时,可能出现下列情况:
    1. 计数器++时少加:
      1. 主线程中智能指针被创建出来,refCount = 1;
      2. 子线程1中对主线程智能指针对象拷贝,refCount还未++完成(++分三个原子步骤)
      3. 子线程2中对主线程智能指针对象拷贝,refCount++完成
      4. 子线程1中refCount++完成
      5. 此时对同一资源的引用计数器本来应该是3,但是只有2
    2. 计数器- -时少减:
      1. 所有线程中只有两个智能指针对象时:
      2. 子线程1中使用完毕智能指针对象,开始析构,但是refCount–未完成(–分三个原子步骤)
      3. 子线程2中也使用完毕智能指针对象,开始析构,refCount- -完成
      4. 子线程2本来refCount- -之后,refCount==0,开始delete。但是此时refCount不为0
      5. 子线程1完成refCount–,资源无人delete,造成内存泄露
  • 线程安全对策:
    1. 加锁
    2. 锁也要借鉴refCount的原理,使用指针完成“一人一把锁”

真·智能指针:

代码:

  • 吸收动态引用计数的两大缺点之后,

    我们终于可以写出基本没有安全问题的智能指针类了:

#include <iostream>
#include <mutex>
using namespace std;
template <class T>
class SharedPtr{private:T *ptr;int *refCount;mutex *mtx;private:void AddRefCount(){mtx.lock();*refCount++;mtx.unlock();}void SubRefCount(){bool flag = 0;mtx.lock();if (--(*refCount) == 0){delete ptr;delete refCount;flag = true;}mtx.unlock();if(flag == 1)delete mtx;}public:SharedPtr(T *_ptr){ptr = _ptr;refCount = new int(0);mtx = new mutex;}//默认采用拷贝构造的对象暂无ptr/refCount/mtx SharedPtr(SharedPtr<T> &sp){ptr = sp.ptr;refCount = sp.refCount;mtx = sp.mtx;AddRefCount();}//默认采用赋值构造的对象已有ptr/refCount/mtx SharedPtr<T>& operator=(SharedPtr<T> &sp){if(ptr != sp.ptr){SubRefCount();ptr = sp.ptr;refCount = sp.refCount;mtx = sp.mtx;AddRefCount();}}~ShardPtr(){SubRefCount();}
};

优点:

  1. 类的基本优点:指针所指资源不用时自动析构释放

  2. 多个智能指针对象共享一块资源:

    每一块资源都独立使用一个引用计数器

    直接指针构造 / 拷贝构造 / 赋值构造 / 析构,都不会出现多重析构

  3. 线程安全:不会出现引用计数器少++ / 少- -的情况

缺点:

  1. 多个智能指针对象直接使用指针构造对象时,还是存在多重析构异常:
int *p = new int(10);
shared_ptr<int> sp1(p);
shared_ptr<int> sp2(p);
shared_ptr<int> sp3(p);
  1. 关于拷贝构造是否要先减少原有资源引用计数器,需要单独为使用者说明:
    1. 如果拷贝构造只能使用在空白对象上,则不需要减少原有资源引用计数器
    2. 如果拷贝构造只能使用已经赋值过的对象上,则需要减少原有资源引用计数器

漏洞:循环引用

  • 查看下面的链表节点智能指针,分析析构过程:
struct ListNode{int data;shared_ptr<ListNode> prev;shared_ptr<LIstNode> next;~ListNode(){cout<<"~ListNode()"<<endl;}
}int main(){shared_ptr<ListNode> node1(new ListNode);shared_ptr<ListNode> node2(new ListNode);node1->next = node2;node2->next = node1;return 0;
}
  • 析构一个节点需要析构三部分

    1. 值date
    2. 前驱指针
    3. 后继指针
  • 当前两块资源的引用计数器状态:
    循环引用

  • 要释放node1,需要资源引用计数器==0

    node1完成refCount–后,refCount==1

    要想refCount继续–,需要释放node2的next

    要释放node2的next,需要释放node2

  • 要释放node2,需要资源引用计数器==0

    node2完成refCount–后,refCount==1

    要想refCount继续–,需要释放node1的prev

    要想释放node1的prev,需要释放node1

  • 发现出现了类似锁套锁的死锁情况,不过这里叫循环引用

    下面来看c++11中解决循环引用的weak_ptr<>

weak_ptr:

原理:

  • 循环引用出现的根本原因:
	node1->next = node2;node2->next = node1;//各自资源引用计数器数目 +1 了
  • weak_ptr避免循环引用的对策:
    1. 增加引用对象时,不会增加资源计数器
    2. 析构引用对象时,也不会发生双重析构

使用:

  • 我们就不自己手动模拟实现了:#include < memory>
struct ListNode{int _data;weak_ptr<ListNode> _prev;weak_ptr<ListNode> _next;~ListNode(){cout << "~ListNode()" << endl;}
};
int main()
{shared_ptr<ListNode> node1(new ListNode);shared_ptr<ListNode> node2(new ListNode);node1->_next = node2;node2->_prev = node1;return 0;
}

定制删除器:

背景:

  • 不论是功能完整的shared_ptr还是特殊场景下的weak_ptr,

    既然称为智能指针,就可以接收很多类型的指针

    但是不同类型指针所指资源释放方式不同:

    1. new开辟的空间的指针 -> delete删除
    2. new[]开辟的空间的指针 -> delete[]删除
    3. malloc开辟的空间的指针 -> free释放
    4. fopen()打开的文件的指针 -> fclose()关闭
  • 所以我们在单纯为这些智能STL类传入指针的同时,也应该传入删除指针的方式,这就叫做定制删除器

删除器类型:

  • 删除功能本质还是一个可调用对象:

    1. 函数名 / 函数指针
    2. 仿函数类对象
    3. lambda表达式
  • 重提一下new delete 和 new[] delete[]的区别:

    1. new出来的空间全部都是存储内容,delete按照类型大小+内存开头地址删除即可
    2. new[]出来的空间开头存储的是元素个数,delete按照元素个数+类型大小+内存开头地址进行删除
    3. 本质上两组关键字看待内存空间的方式不一致,即使是一个bit的区别,造成的差异也很大
    4. 但是现在很多编译器做了优化,new[]出来的含有个数的空间,也可以被delete删除释放

模拟实现:

  • 由于类内要保存传入的删除器函数及其类型,所以只能采用模板仿函数类,而不能采用函数参数:
template <class T>
class default_delete{public:void operator()(const T*ptr){cout<<"delete:"<<ptr<<endl;delete ptr;}
};
template <class T, class D = default_delete<T>>
class del_ptr{private:T *ptr;public:unique_ptr(T *_ptr){ptr = _ptr;}~unique_ptr(){if(ptr){D del;del(ptr);}}
};
struct DeleteArray{void operator()(A* ptr){cout<< "delete[] : "<<ptr <<endl;delete[] ptr;}
};
struct DeleteFile{void operator()(FILE* ptr){cout<< "fclose[] : "<<ptr <<endl;fclose(ptr);}
};
int main(){del_ptr<A> sp1(new A);	//默认删除器del_ptr<A, DeleteArray> sp2(new A[10]);del_ptr<A, DeleteFile> sp3(fopen("test.txt", "r"));
}

STL中的删除器:

  • STL实现较为复杂,同时支持:
    1. 函数参数传入删除器函数
    2. 模板类内保存删除器模板仿函数类

模板传入仿函数类:

  • 模板仿函数类:
struct DeleteArray{void operator()(A* ptr){cout<< "delete[] : "<<ptr <<endl;delete[] ptr;}
};
int main(){unique_ptr<A> sp1(new A);unique_ptr<A, DeleteArray> sp1(new A);return 0;
}

传参传入删除函数:

  • 传参传入删除函数:
int main(){unique_ptr<A> sp1(new A[10], [](A* p){delete[] p;},);unique_ptr<A> sp2(fopen("test.txt","r"), [](FILE *p){fclose(p);});
}

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

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

相关文章

报告分享|2022汽车生态营销白皮书

报告链接&#xff1a;http://tecdat.cn/?p28679 不确定性增加&#xff0c;消费意愿在压力下等待释放 今年3月以来&#xff0c;受国际冲突和国内环境双重影响&#xff0c;能源价格大幅上涨&#xff0c;导致全球供应不稳定趋势加深&#xff0c;宏观经济下行压力明显&#xff0…

程序设计竞赛-过了这个村没这个店

文章目录个人经验竞赛简介蓝桥杯天梯赛CCPCICPC其他个人经验 初闻不知曲中意&#xff0c;再闻已是曲中人。 标题无意夸张&#xff0c;但是竞赛生涯的时间真的不长&#xff0c;机会真的错过了就没有了。一般来说&#xff0c;大一打基础&#xff0c;学习编程语言、数据结构和算法…

Moment.js的常用函数、借助vue和Moment.js实现一个简单的时钟

前言 项目中关于时间的处理是挺常见的&#xff0c;虽然之前就知道有Moment.js这个库&#xff0c;但是一直没有接触过。只不过最近同事在项目中使用了&#xff0c;那也只能简单学习一下&#xff0c;不然遇到了完全看不懂。 本文只介绍一下常用的函数&#xff0c;其他内容可以在…

想换工作?那还不赶紧来看看这份面试题

引言 “寒冬”之下&#xff0c;诸如 “Android 凉了”之类的话我已经屡见不鲜了&#xff0c;现在互联网行业的热潮已经褪去&#xff0c;开始恢复冷静&#xff1b;这样一来&#xff0c;互联网公司就会面向大量的开发者们&#xff0c;因此对应的要求只会越来越高&#xff1b;据反…

用纯css实现一个图片拼接九宫格

<style> body{ margin: 0; padding: 0; // 设定居中 display: flex; justify-content: center; align-items: center; height: 100vh; } .container{ width: 300px; height: 300px; display: flex; // 子盒子布局&#xff0c;要让子盒子之间有间隙就把宽高设大一些。 jus…

报告分享|2022年中国机器人产业图谱及云上发展研究报告

报告链接&#xff1a;http://tecdat.cn/?p28681 报告在分析当前我国机器人市场现状与产业图谱的基础上&#xff0c;对人工智能、5G、云计算、边缘计算等新兴技术赋能机器人智能化、轻量化、柔性化发展进行了理性探讨&#xff0c;结合阿里云加速器企业案例探讨了机器人企业的上…

连接查询-mysql详解(五)

上篇文章说了&#xff0c;mysql5.6.6版本之前数据默认在系统表空间&#xff0c;之后默认在独立表空间&#xff0c;innodb因为索引和数据在一个b树&#xff0c;所以两个文件&#xff0c;一个文件结构&#xff0c;一个存数据&#xff0c;myISAM则是三个文件。一个聚簇索引有两个段…

小程序云开发学习笔记

小程序云开发学习笔记 初始化 在app.js里面 小程序一开始就初始化&#xff0c;多次调用只有第一次触发 onLaunch() { console.log("小程序打开"); wx.cloud.init({ env: ayang-8g50ew302a3a6c5a, //云开发id }) } 数据库操作 查询&#xff08;一定要配置数据权限&a…

高等工程数学 —— 第一章 (1)距离与范数

前言 研一生活开始了&#xff0c;看了大家对我之前博客的鼓励让我知道写博客是一件多么有意义的事情。写这些让我遇见许多陌生的有缘人&#xff0c;有老骥伏枥的大叔、也有可爱温暖的学妹…… 这里将高等工程数学的笔记留给不爱吃香菜的月亮&#xff0c;希望这些陪伴过我的微光…

ElasticSearch(四)【高级查询】

四、高级查询 说明 ES中提供了一种强大的检索数据方式&#xff0c;这种检索方式称之为Query DSL&#xff0c;Query DSL是利用Rest API传递JSON格式的请求体&#xff08;Request Body&#xff09;数据与ES进行交互&#xff0c;这种方式的丰富查询语法让ES检索变得更强大&#xf…

Grafana alert预警+钉钉通知

1 Grafana alert预警 如下图所示&#xff0c;主要是前3步&#xff0c;设置alert rules、contact points 、notification policies。alert rules主要设置触发警告的规则&#xff1b;contact points设置通过什么发送预警&#xff0c;如钉钉&#xff1b;notification policies 将…

哲学家干饭问题 C++

哲学家干饭问题 C 哲学家就餐问题可以这样表述&#xff0c;假设有五位哲学家围坐在一张圆形餐桌旁&#xff0c;做以下两件事情之一&#xff1a;吃饭&#xff0c;或者思考。吃东西的时候&#xff0c;他们就停止思考&#xff0c;思考的时候也停止吃东西。餐桌上有五碗意大利面&am…

Vue2.0到3.0的过渡,setup,ref函数,reactive函数,计算属性computed

setup 1、Vue3.0的组件中所有用到的:数据、方法等等&#xff0c;均要配置在setup中&#xff0c;若要使用里面的数据&#xff0c;可以用return将其返回出来 2、若在setup中返回的是一个对象&#xff0c;则对象中的数据、方法、在模板中均可直接使用 例如 <template><di…

[Git] 系列三随意修改提交记录以及一些技巧

[Git] 系列三随意修改提交记录以及一些技巧 Author: Xin Pan Date: 2022.09.17 文章目录[Git] 系列三随意修改提交记录以及一些技巧整理提交记录未知提交号哈希值时怎么办&#xff1f;一些技巧本地栈式提交方法一方法二TagDescribe高级命令总结好了&#xff0c;大概总结好了。…

搭建游戏要选什么样的服务器?

服务器是游戏平台数据传输的重要载体&#xff0c;事关我们游戏创业发展的稳定性、安全性。那么&#xff0c;游戏平台搭建要选什么服务器&#xff1f;有什么参考指标&#xff1f;本文将带领大家一探究竟&#xff01; 首先是“游戏平台搭建要选择什么服务器”&#xff0c;我们可…

论文阅读_对比学习_SimCSE

英文题目&#xff1a;SimCSE: Simple Contrastive Learning of Sentence Embeddings 中文题目&#xff1a;SimSCE&#xff1a;用简单的对比学习提升句嵌入的质量 论文地址&#xff1a;https://export.arxiv.org/pdf/2104.08821.pdf 领域&#xff1a;自然语言处理&#xff0c;对…

Redis的基本使用

1.Redis简介 &#xff08;1&#xff09;什么是Redis ①Redis是一个基于内存的key-value结构数据库 ②基于内存存储&#xff0c;读写性能高 ③适合存储热点数据(热点商品、资讯、新闻) ④Redis是一个开源的内存中的数据结构存储系统&#xff0c;它可以用作&#xff1a;数据库、…

计组--存储系统

存储系统 思维导图&#xff1a; 存储器概述 存储器的分类 按在计算机中的作用(层次)分类 主存储器&#xff0c;简称主存(内存) 存放计算机运行期间所需的程序和数据&#xff0c;CPU可以直接对其进行访问。 辅助存储器&#xff0c;简称辅存(外存) 辅存的内容需要调入主存后才…

普中A6开发版——XPT2046四引脚切换测量(含详细教程以及原理图等资料)

文章目录一、简介二、原理图以及手册三、接线四、选择数码管芯片原理讲解五、代码一、简介 本文介绍了XPT2046的使用方法以及普中A6开发版的接线等&#xff0c;并从原理图以及手册中摘选了详细的介绍&#xff0c;充分理解其工作原理。XPT2046本来是一个电阻式触摸屏控制器&…

监控系统架构方案

前言 对于企业级服务器管理&#xff0c;站群管理&#xff0c;针对服务器的监控是非常必要的。 通常&#xff0c;在电脑出现卡死&#xff0c;或进程停止或被挂起的情况下&#xff0c;大家都会使用任务管理器查看进程情况。针对电脑流畅性或资源优化&#xff0c;通常会使用资源管…