四种常见智能指针的介绍

一、介绍

当类中有指针成员时,一般有两种方式来管理指针成员:一是采用值型的方式管理,每个类对象都保留一份指针指向的对象的拷贝;另一种更优雅的方式是使用智能指针(smart pointer),从而实现指针指向的对象的共享。

智能指针是存储指向动态分配(堆)对象指针的类。除了能够在适当的时间自动删除指向的对象外,他们的工作机制很像C++的内置指针。智能指针在面对异常的时候格外有用,因为他们能够确保正确的销毁动态分配的对象。他们也可以用于跟踪被多用户共享的动态分配对象。

一句话简单概括:智能指针是帮助我们管理动态内存分配的,会自动帮忙释放new出来的内存,从而避免内存泄漏的问题。

二、为什么要使用智能指针

答:使用智能指针可以有效的避免内存泄漏。

什么是内存泄漏?

答:内存泄漏指因为疏忽或错误造成程序未能释放已经不再使用的内存的情况。内存泄漏并不是指内存在物理上的消失,而是应用程序分配某段内存后,因为设计错误,失去了对该段内存的控制,因而造成了内存的浪费。

危害:长期运行的程序出现内存泄漏,影响很大,如操作系统、后台服务等等,出现内存泄漏会导致响应越来越慢,最终卡死。

如下例子:人为原因导致忘记释放;代码逻辑原因抛出异常使内存释放执行不了。

void MemoryLeaks()
{// 1.内存申请了忘记释放int* p1 = (int*)malloc(sizeof(int));int* p2 = new int;// 2.异常安全问题int* p3 = new int[10];Func(); // 这里Func函数抛异常导致 delete[] p3未执行,p3没被释放.delete[] p3;}

常见的内存泄漏

堆内存泄漏(Heap leak)
堆内存指的是程序执行中依据须要分配通过malloc / calloc / realloc / new等从堆中分配的一块内存,用完后必须通过调用相应的 free或者delete 释放。假设程序的设计错误导致这部分内存没有被释放,那么以后这部分空间将无法再被使用,就会产生Heap Leak。
系统资源泄漏
指程序使用系统分配的资源,比如套接字、文件描述符、管道等没有使用对应的函数释放掉,导致系统资源的浪费,严重可导致系统效能减少,系统执行不稳定。

三、智能指针的使用及原理

RAII思想

当我们使用一块资源时,一般会经历三个步骤:a.获取资源(创建对象);b.使用资源;c.销毁资源(析构对象)。但我们通常会忘记c销毁资源,从而导致一些内存问题的产生,而RAII的思想,就可以巧妙的解决c步骤的发生。

RAII(Resource Acquisition Is Initialization)是一种利用对象生命周期来控制程序资源(如内存、文件句柄、网络连接、互斥量等等)的简单技术 。在对象构造时获取资源,接着控制对资源的访问使之在对象的生命周期内始终保持有效,最后在对象析构的时候释放资源。实际上是把管理一份资源的责任托管给了一个对象。这种做法有两大好处:不需要显式地释放资源;采用这种方式,对象所需的资源在其生命期内始终保持有效 。

原理

智能指针本质就是一个类模板,它可以创建任意的类型的指针对象,当智能指针对象使用完后,对象就会自动调用析构函数去释放该指针所指向的空间。

即具有RAII思想,且重载有operator*和operator->像指针一样的行为的一种指针类型。

一个智能指针模板类,至少具有如下格式:

template<class T>
class SmartPtr {
public:SmartPtr(T* ptr = nullptr)  // 构造: _ptr(ptr){}~SmartPtr()  // 释放{if(_ptr)delete _ptr;}T& operator*() {return *_ptr;}  // 重载 *T* operator->() {return _ptr;}  // 重载 ->
private:T* _ptr;
};

四、常见的智能指针类型

头文件 #include <memory>

1.auto_ptr

C++98版本的库中就提供了auto_ptr的智能指针,其实现原理是管理权转移的思想。

这就导致了很不好的情况产生如下代码,将对象p1转移给p2时,不会发生任何问题,但是当访问到被转移后的p1时,就会报错。

auto_ptr<string> p1(new string("I reigned loney as a cloud."));
auto_ptr<string> p2;
p2=p1; //auto_ptr不会报错
cout << *p2 << endl;  // 访问没有问题
cout << *p1 << endl;  // 会报错

在p1赋值给p2后,首先p2会先将自己原先托管的指针释放掉,然后接收托管p1所托管的指针,然后p1所托管的指针置NULL,也就是p2托管了p1的指针,而p1放弃了托管,所以再次访问到p1时,就会发生错误。

即auto_ptr:存在潜在的内存崩溃问题。所以在C++11中,auto_ptr已经被弃用了。并且企业中也会禁止使用auto_ptr。

模拟实现:

template<class T>
class auto_ptr
{
public:auto_ptr(T* ptr = nullptr): _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)  // 先释放自己的空间,再获取别的对象{std::cout << "Delete:" << _ptr << std::endl;delete _ptr;}// 转移ap中资源到当前对象中_ptr = ap._ptr;ap._ptr = nullptr;}return *this;}~auto_ptr(){if (_ptr){std::cout << "Delete:" << _ptr << std::endl;delete _ptr;}}T& operator*(){return *_ptr;}T* operator->(){return _ptr;}private:T* _ptr;
};

2.unique_ptr

unique_ptr避免了auto_ptr中出现的问题,而它的解决方法也更简单粗暴,即禁用拷贝赋值。

unique_ptr(const unique_ptr<T>& sp) = delete;
unique_ptr<T>& operator=(const unique_ptr<T>& sp) = delete;

unique_ptr实现独占式拥有或严格拥有概念,保证同一时间内只有一个智能指针可以指向该对象。它对于避免资源泄露,例如,以new创建对象后因为发生异常而忘记调用delete时的情形特别有用。

unique_ptr特性

  1. 基于排他所有权模式:两个指针不能指向同一个资源。
  2. 无法进行左值unique_ptr复制构造,也无法进行左值复制赋值操作,但允许临时右值赋值构造和赋值。
  3. 保存指向某个对象的指针,当它本身离开作用域时会自动释放它指向的对象。
  4. 在容器中保存指针是安全的。
unique_ptr<string> pu1(new string ("hello world"));
unique_ptr<string> pu2;
pu2 = pu1; // #1 不允许
unique_ptr<string> pu3;
pu3 = unique_ptr<string>(new string ("You")); // #2 允许

当程序试图将一个 unique_ptr 赋值给另一个时,如果源unique_ptr 是个临时右值(#2),编译器允许这么做;如果源 unique_ptr 将存在一段时间,编译器将禁止这么做(#1)。

其中#1留下悬挂的unique_ptr(pu1),这可能导致危害。而#2不会留下悬挂的unique_ptr,因为它调用 unique_ptr 的构造函数,该构造函数创建的临时对象在其所有权让给 pu3 后就会被销毁。这种随情况而已的行为表明,unique_ptr 优于允许两种赋值的auto_ptr 。

另外:如果确实想执行类似与#1的操作,要安全的重用这种指针,可给它赋新值。C++11标准中的库函数std::move(),能够允许将一个unique_ptr赋给另一个。

std::unique_ptr<std::string> p1(new std::string("test move copy"));
std::unique_ptr<std::string> p2 = std::move(p1);
std::cout << p1.get() << std::endl;
std::cout << p2.get() << std::endl;

但是,通过运行结果,可以看出,赋值后,p1还是被置空了,结果与auto_ptr一致了。所以,unique_ptr会禁止拷贝复制。

与auto_ptr区别:禁用了拷贝赋值操作。

模拟实现:

template<class T>
class unique_ptr
{
private:
public:unique_ptr(T* ptr = nullptr): _ptr(ptr){}// 防拷贝 C++11, 禁用拷贝赋值unique_ptr(unique_ptr<T>& ap) = delete;unique_ptr<T>& operator=(unique_ptr<T>& ap) = delete;~unique_ptr(){if (_ptr){cout << "Delete:" << _ptr << endl;delete _ptr;}}T& operator*(){return *_ptr;}T* operator->(){return _ptr;}private:T* _ptr;
};

3.shared_ptr

原理:

shared_ptr的原理:是通过引用计数的方式来实现多个shared_ptr对象之间共享资源。 是为了解决 auto_ptr 在对象所有权上的局限性(auto_ptr 是独占的), 在使用引用计数的机制上提供了可以共享所有权的智能指针。

  1. shared_ptr在其内部,给每个资源都维护了一份计数,用来记录该份资源被几个对象共享,发生拷贝或赋值时+1。可以通过成员函数use_count()来查看资源的所有者个数 。
  2. 对象被销毁时(也就是析构函数调用),就说明自己不使用该资源了,对象的引用计数减一。
  3. 如果引用计数是0,就说明自己是最后一个使用该资源的对象,必须释放该资源
  4. 如果不是0,就说明除了自己还有其他对象在使用该份资源,不能释放该资源,否则其他对象就成野指针了。

成员函数:

  • use_count 返回引用计数的个数。
  • unique 返回是否是独占所有权( use_count 为 1)。
  • swap 交换两个 shared_ptr 对象(即交换所拥有的对象)。
  • reset 放弃内部对象的所有权或拥有对象的变更, 会引起原有对象的引用计数的减少。
  • get 返回内部对象(指针), 由于已经重载了()方法, 因此和直接使用对象是一样的.如 shared_ptr sp(new int(1)); sp 与 sp.get()是等价的。

use_count 使用:

int main()
{std::shared_ptr<std::string> sp1;std::shared_ptr<std::string> sp2(new std::string("test"));std::cout << "sp1	use_count() = " << sp1.use_count() << std::endl;    // 未发生拷贝或复制 0std::cout << "sp2	use_count() = " << sp2.use_count() << std::endl << std::endl;  // 拷贝1次 +1// 共享sp1 = sp2;  // 赋值一次,sp2 +1,sp1内部变为sp2,所以结果应该与sp2一致std::cout << "sp1	use_count() = " << sp1.use_count() << std::endl;  // 打印 2std::cout << "sp2	use_count() = " << sp2.use_count() << std::endl << std::endl;  // 打印 2std::shared_ptr<std::string> sp3(sp1);  // sp3拷贝sp1; sp1 次数+1,sp3与sp1 一致std::cout << "sp1	use_count() = " << sp1.use_count() << std::endl;  // 打印 3std::cout << "sp2	use_count() = " << sp2.use_count() << std::endl;  // 打印 3std::cout << "sp2	use_count() = " << sp3.use_count() << std::endl << std::endl;  // 打印 3return 0;
}

运行结果:

可以发现,三次发生计数+1操作的位置在sp2(new std::string("test"))sp1 = sp2; sp3(sp1)三个拷贝、赋值的地方。

reset使用:

int main()
{std::shared_ptr<std::string> sp1;std::shared_ptr<std::string> sp2(new std::string("test"));std::cout << "sp1	use_count() = " << sp1.use_count() << std::endl;    // 未发生拷贝或复制 0std::cout << "sp2	use_count() = " << sp2.use_count() << std::endl << std::endl;  // 拷贝1次 +1// 共享sp1 = sp2;  // 赋值一次,sp2 +1,sp1内部变为sp2,所以结果应该与sp2一致std::cout << "sp1	use_count() = " << sp1.use_count() << std::endl;  // 打印 2std::cout << "sp2	use_count() = " << sp2.use_count() << std::endl << std::endl;  // 打印 2std::shared_ptr<std::string> sp3(sp1);  // sp3拷贝sp1; sp1 次数+1,sp3与sp1 一致std::cout << "sp1	use_count() = " << sp1.use_count() << std::endl;  // 打印 3std::cout << "sp2	use_count() = " << sp2.use_count() << std::endl;  // 打印 3std::cout << "sp2	use_count() = " << sp3.use_count() << std::endl << std::endl;  // 打印 3// reset使用sp1.reset();std::cout << "sp1	use_count() = " << sp1.use_count() << " " << sp1 << std::endl;std::cout << "sp2	use_count() = " << sp2.use_count() << " " << sp2 << std::endl;std::cout << "sp3	use_count() = " << sp3.use_count() << " " << sp3 << std::endl << std::endl;return 0;
}

由运行结果可见,sp1调用reset后,sp1本身被置空,它对应的计数也变为0,而sp2和sp3,会随着sp1调用reset导致计数会减1。

unique使用:

std::shared_ptr<int> p1(new int(10));
std::shared_ptr<int> p2 = p1;// #1
std::cout << "p1 is unique: " << std::boolalpha << p1.unique() << std::endl; // 输出:p1 is unique: false
std::cout << "p2 is unique: " << std::boolalpha << p2.unique() << std::endl; // 输出:p2 is unique: false// #2
std::shared_ptr<int> p3(new int(20));
std::cout << "p3 is unique: " << std::boolalpha << p3.unique() << std::endl; // 输出:p3 is unique: true// #3
p2.reset();
std::cout << "p1 is unique: " << std::boolalpha << p1.unique() << std::endl; // 输出:p1 is unique: true
std::cout << "p2 is unique: " << std::boolalpha << p2.unique() << std::endl; // 输出:p2 is unique: false

#1 p1进行了一次赋值给p2,所以发生了一次共享,所以不是独占所有权false。

#2 p3,它指向一个新的int类型的对象,此时p3是该对象的唯一所有者。

#3 使用reset函数将p2重置为空,此时p1是该对象的唯一所有者,unique函数返回true。由于p2已经被置空,它不再指向任何资源,输出为false。

swap使用

    // 创建两个 shared_ptr 对象std::shared_ptr<int> ptr1 = std::make_shared<int>(42);std::shared_ptr<int> ptr2 = std::make_shared<int>(100);std::shared_ptr<int> ptr3 = ptr1;std::cout << "ptr1: " << *ptr1 << ", use_count: " << ptr1.use_count() << std::endl;std::cout << "ptr2: " << *ptr2 << ", use_count: " << ptr2.use_count() << std::endl;// 使用 swap 交换资源ptr1.swap(ptr2);std::cout << "ptr1: " << *ptr1 << ", use_count: " << ptr1.use_count() << std::endl;std::cout << "ptr2: " << *ptr2 << ", use_count: " << ptr2.use_count() << std::endl;

由运行结果可以看出,ptr1.swap(ptr2);执行后,ptr1与ptr2对应的值以及计数发生了交换。

shared_ptr的线程安全问题

shared_ptr的线程安全分为两方面:

  1. 智能指针对象中引用计数是多个智能指针对象共享的,两个线程中智能指针的引用计数同时++或--,这个操作不是原子的,引用计数原来是1,++了两次,可能还是2这样引用计数就错乱了。会导致资源未释放或者程序崩溃的问题。所以只能指针中引用计数++、--是需要加锁的,也就是说引用计数的操作是线程安全的。
  2. 智能指针管理的对象存放在堆上,两个线程中同时去访问,会导致线程安全问题。

模拟实现:

template<class T>
class shared_ptr
{
public:shared_ptr(T* ptr = nullptr): _ptr(ptr), _pCount(new int(1)){}void Release(){if (--(*_pCount) == 0){cout << "Delete:" << _ptr << endl;delete _ptr;delete _pCount;}}~shared_ptr(){Release();}// sp1(sp2)shared_ptr(const shared_ptr<T>& sp): _ptr(sp._ptr), _pCount(sp._pCount){(*_pCount)++;}shared_ptr<T>& operator=(const shared_ptr<T>& sp){//if (this == &sp)if (_ptr == sp._ptr){return *this;}// 减减被赋值对象的计数,如果是最后一个对象,要释放资源/*if (--(*_pCount) == 0){delete _ptr;delete _pCount;}*/Release();// 共管新资源,++计数_ptr = sp._ptr;_pCount = sp._pCount;(*_pCount)++;return *this;}T& operator*(){return *_ptr;}T* operator->(){return _ptr;}private:T* _ptr;// 引用计数int* _pCount;
};

4.weak_ptr

weak_ptr 是一种不控制对象生命周期的智能指针, 它指向一个 shared_ptr 管理的对象。进行该对象的内存管理的是那个强引用的 shared_ptr。weak_ptr只是提供了对管理对象的一个访问手段。weak_ptr 设计的目的是为配合 shared_ptr 而引入的一种智能指针来协助 shared_ptr 工作,它只可以从一个 shared_ptr 或另一个 weak_ptr 对象构造, 它的构造和析构不会引起引用记数的增加或减少。

weak_ptr是用来解决shared_ptr相互引用时的死锁问题,如果说两个shared_ptr相互引用,那么这两个指针的引用计数永远不可能下降为0,资源永远不会释放。它是对对象的一种弱引用,不会增加对象的引用计数,和shared_ptr之间可以相互转化,shared_ptr可以直接赋值给它,它可以通过调用lock函数来获得shared_ptr。

class B;
class A
{public :std::shared_ptr<B> pb_;~A(){std::cout << "A delete\n";}
};
class B
{public :std::shared_ptr<A> pa_;~B(){std::cout << "B delete\n";}
};
void fun()
{std::shared_ptr<B> pb(new B());std::shared_ptr<A> pa(new A());pb->pa_ = pa;pa->pb_ = pb;std::cout << pb.use_count() << std::endl;std::cout << pa.use_count() << std::endl;
} int main()
{fun();return 0;
}

可以看到fun函数中pa ,pb之间互相引用,两个资源的引用计数为2,当要跳出函数时,智能指针pa,pb析构时两个资源引用计数会减一,但是两者引用计数还是为1,导致跳出函数时资源没有被释放(A B的析构函数没有被调用):

(没有调用析构)

如果把其中一个改为weak_ptr就可以了,我们把类A里面的shared_ptr pb_; 改为weak_ptr pb; 运行结果如下:

这样的话,资源B的引用开始就只有1,当pb析构时,B的计数变为0,B得到释放,B释放的同时也会使A的计数减一,同时pa析构时使A的计数减一,那么A的计数为0,A得到释放。

注意:我们不能通过weak_ptr直接访问对象的方法,比如B对象中有一个方法print(),我们不能这样访问,pa->pb->print(); 英文pb是一个weak_ptr,应该先把它转化为shared_ptr,如:shared_ptr p = pa->pb_.lock(); p->print();

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

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

相关文章

统信系统常见问题解决方法

☞ ░ 前往老猿Python博客 ░ https://blog.csdn.net/LaoYuanPython 背景说明 本文所说的问题&#xff0c;是基于浪潮统信UOS的环境存在的问题。 一、WPS新建文档默认保存格式不对 解决办法&#xff1a; 1.编辑/opt/apps/cn.wps.wps-office-pro/files/kingsoft/wps-office/…

UML类图学习

UML类图学习 UML类图是描述类之间的关系概念1.类(Class)&#xff1a;使用三层矩形框表示2.接口(interface)&#xff1a;使用两层矩形框表示&#xff0c;与类图主要区别在于顶端有<<interface>>显示3、继承类&#xff08;extends&#xff09;&#xff1a;用空心三角…

蓝桥杯C/C++程序设计——单词分析

题目描述 小蓝正在学习一门神奇的语言&#xff0c;这门语言中的单词都是由小写英文字母组 成&#xff0c;有些单词很长&#xff0c;远远超过正常英文单词的长度。小蓝学了很长时间也记不住一些单词&#xff0c;他准备不再完全记忆这些单词&#xff0c;而是根据单词中哪个字母出…

Pytest 项目结合Jenkins

一、window安装centos7虚拟机 参考网上其他教程 二、Linux安装Jenkins 进入jenkins.io网址&#xff0c;点击download&#xff0c;选择CentOS版本 1、Linux中安装java环境和git Jenkins的运行需要java环境&#xff1b;安装git是为代码上传给仓库做准备&#xff1b; yum - y…

C语言-环境搭建

文章目录 内容Notepad的安装gcc编译工具的配置 编写软件的安装&#xff1a;软件传送门&#xff1a;Notepad软件选择一个合适的路径&#xff0c;一键傻瓜式安装即可 编译工具gcc在windows环境下的配置&#xff1a;解压gcc编辑工具包解压出来的mingw64文件放到一个合适的磁盘路径…

华为HCIE-Datacom课程介绍

厦门微思网络HCIE-Datacom课程介绍 一、认证简介 HCIE-Datacom&#xff08;Huawei Certified ICT Expert-Datacom&#xff09;认证是华为认证体系中的顶级认证&#xff0c;HCIE-Datacom认证定位具备坚实的企业网络跨场景融合解决方案理论知识&#xff0c;能够使用华为数通产品…

副业类小报童热门专栏TOP15

今天介绍15个副业小报童&#xff0c;可以说是当前小报童平台&#xff0c;副业类专栏的天花板内容了 这些专栏&#xff0c;都有免费内容可以查看&#xff0c;而且还是3天无理由退款的&#xff0c;完全可以尝试着订阅一波 关键单价都非常亲民&#xff0c;怎么都不亏&#xff01…

安装与部署Hadoop

一、前置安装准备1、机器2、java3、创建hadoop用户 二、安装Hadoop三、环境配置1、workers2、hadoop-env.sh3、core-site.xml4、hdfs-site.xml5、linux中Hadoop环境变量 四、启动hadoop五、验证 一、前置安装准备 1、机器 主机名ip服务node1192.168.233.100NameNode、DataNod…

工智能基础知识总结--什么是CRF

什么是CRF CRF是给定随机变量X的条件下,随机变量Y的马尔科夫随机场(概率无向图)。 这里主要介绍在线性链上的特殊的条件随机场,称为线性链条件随机场,可用于序列标注等问题。 线性链条件随机场如下图所示,输出变量仅与输入变量以及相邻输出变量有连线。 CRF的参数化表示…

【网络安全】有趣的基础知识

背景 逐条记录网络安全学习中有趣的内容和知识。 内容 CNNIC&#xff08;中国互联网络信息中心&#xff09;是中国国家域名.cn的管理组织。中国互联网络信息中心于1997年6月3日组建&#xff0c;现为工业和信息化部 直属事业单位 &#xff0c;行使国家互联网络信息中心职责。…

政务大数据能力平台建设方案:文件全文30页,附下载

关键词&#xff1a;智慧政务解决方案&#xff0c;智慧政务建设&#xff0c;智慧政务服务平台&#xff0c;智慧政务大数据&#xff0c;数字政务一体化平台。大数据&#xff0c;政务大数据建设 一、智慧政务建设需求 1、政务服务需求&#xff1a;智慧政务建设需要满足人民群众的…

详解Vue3中的鼠标事件click和dblclick

本文主要介绍Vue3中的常见鼠标事件。 目录 一、click——单击事件二、dblclick——双击事件三、在使用click和dbclick需要注意的地方 下面是Vue 3中常用的鼠标事件&#xff1a; 一、click——单击事件 click事件是一种常见的事件类型&#xff0c;用于在用户点击某个元素时触发…

MFC扩展库BCGControlBar Pro v34.0 - 仪表盘控件全面升级

BCGControlBar库拥有500多个经过全面设计、测试和充分记录的MFC扩展类。 我们的组件可以轻松地集成到您的应用程序中&#xff0c;并为您节省数百个开发和调试时间。 BCGControlBar专业版 v34.0已正式发布了&#xff0c;该版本包括新的主题任务对话框、图像效果、旋转圆形刻度、…

2023 年四川省职业院校技能大赛“信息安全管理与评估”样题

2023 年四川省职业院校技能大赛&#xff08;高等职业教育&#xff09; “信息安全管理与评估”样题 竞赛需要完成三个阶段的任务&#xff0c;分别完成三个模块&#xff0c;总分共计 1000分。三个模块内容和分值分别是&#xff1a; 第一阶段&#xff1a;模块一 网络平台搭建与设…

VitulBox中Ubuntu虚拟机安装JAVA环境——备赛笔记——2024全国职业院校技能大赛“大数据应用开发”赛项

前言 在进行之后操作是请下载好JDK&#xff0c;之后的内容是以Ubuntu虚拟机中安装java环境续写。 提示&#xff1a;以下操作是在虚拟机hadoop用户下操作的&#xff0c;并为安装java环境作准备 一、更新APT 为了确保Hadoop安装过程顺利进行&#xff0c;建议用hadoop用户登录…

python统计分析——协方差和pearson相关系数

参考资料&#xff1a;用python动手学统计学 使用数据见代码&#xff1a; dic{"x":[18.5,18.7,19.1,19.7,21.5,21.7,21.8,22.0,23.4,23.8],"y":[34,39,41,38,45,41,52,44,44,49] } cov_datapd.DataFrame(dic) 变量x、y的协方差Cov(x,y)的计算公式如下&am…

【Redis-10】Redis集群的实现原理和实践

Redis集群是Redis提供的分布式数据库方案&#xff0c;通过分片来进行数据共享&#xff0c;实现复制和故障转移的功能。 1. Redis集群节点 一个Redis集群由多个节点组成&#xff0c;多个节点可以通过命令实现连接&#xff0c;由独立状态转为集群状态&#xff0c;命令是cluster …

软件测试/测试开发丨Python 常用第三方库 yaml

YAML 一种数据序列化格式用于人类的可读性和与脚本语言的交互一种被认为可以超越 XML、JSON 的配置文件 YAML 基本语法规则 大小写敏感使用缩进表示层级关系缩进时不允许使用 Tab 键&#xff0c;只允许使用空格缩进的空格数目不重要&#xff0c;只要相同层级的元素左侧对齐即…

docker搭建minio集群,集群分享文件URL踩坑问题

一、环境准备 3台机器&#xff0c;Ip地址依次为IP1,IP2,IP3二、设置服务器时间同步 Minio集群需要各个节点的时间保持同步&#xff0c;使用NTP作为时间同步服务&#xff0c;这里以Minio-1&#xff08;IP1&#xff09;为上游服务器&#xff0c;其它2个节点为下游服务器&#x…

微服务(10)

目录 46.k8s中镜像的下载策略是什么&#xff1f; 47.image的状态有哪些&#xff1f; 48.如何控制滚动更新过程&#xff1f; 49.DaemonSet资源对象的特性&#xff1f; 50.说说你对Job这种资源对象的了解&#xff1f; 46.k8s中镜像的下载策略是什么&#xff1f; 可通过命令k…