当前位置: 首页 > news >正文

C++新特性:智能指针

一 、为什么需要智能指针

智能指针主要解决以下问题:
1)内存泄漏:内存手动释放,使用智能指针可以自动释放

2)共享所有权指针的传播和释放,比如多线程使用同一个对象时析构问题,例如同样的数据帧,但是业务A和业务B处理的逻辑不一样(都是只读)。可以用shared_ptr共享数据帧对象的所有权。线程A释放的时候,shared_ptr的引用计数count - 1,当为0的时候释放数据帧对象指针。
在这里插入图片描述

主要类型:
C++里面的四个智能指针: auto_ptrshared_ptrunique_ptrweak_ptr ,其中后三个是C++11支持,并且第一个已经被C++11弃用。

几个指针的特点:
1)unique_ptr:独占对象的所有权,由于没有引用计数,因此性能较好。
2)shared_ptr:共享对象的所有权,但性能略差。
3)weak_ptr:配合shared_ptr,解决循环引用的问题。

二、shared_ptr 共享的智能指针

2.1 内存模型

shared_ptr 内部包含两个指针,一个指向对象,另一个指向控制块(control block),控制块中包含一个引用计数(reference count), 一个弱计数(weak count)和其它一些数据。
在这里插入图片描述
例如对于

std::shared_ptr<int> p1(new int(1));
std::shared_ptr<int> p2 = p1;

在这里插入图片描述
std::shared_ptr使用引用计数,每一个shared_ptr的拷贝都指向相同的内存。再最后一个shared_ptr析构的时候,内存才会被释放。

shared_ptr共享被管理对象,同一时刻可以有多个shared_ptr拥有对象的所有权,当最后一个shared_ptr对象销毁时,被管理对象自动销毁。

2.2 常用函数

s.get()			// 返回shared_ptr中保存的裸指针;
s.reset(...)	// 重置shared_ptr
s.use_count() 	// 返回shared_ptr的强引用计数;
s.unique() 		// use_count()为1,返回true,否则返回false。

reset( )不带参数时,若智能指针s是唯一指向该对象的指针,则释放,并置空。若智能指针P不是唯一指向该对象的指针,则引用计数减少1,同时将P置空。

reset( )带参数时,若智能指针s是唯一指向对象的指针,则释放并指向新的对象。若P不是唯一的指针,则只减少引用计数,并指向新的对象。

2.3 初始化

通过构造函数、reset()make_shared方法来初始化shared_ptr,代码如下:

std::shared_ptr<int> p1(new int(1));
std::shared_ptr<int> p2 = p1;// 对于一个未初始化的智能指针,可以通过reset方法来初始化
// 当智能指针有值的时候调用reset会引起引用计数减1
std::shared_ptr<int> p3;
p3.reset(new int(1));
if(p3) {
cout << "p3 is not null";
}

我们应该优先使用make_shared来构造智能指针,因为他更高效。

auto sp1 = make_shared<int>(100);
或
shared_ptr<int> sp1 = make_shared<int>(100);
//相当于
shared_ptr<int> sp1(new shared_ptr<int> sp1(new int(100)););

这是因为为了节省一次内存分配,原来 shared_ptr<Foo> x(new Foo); 需要为 Foo 和 ref_count 各分配一次内存,现在用make_shared()的话,可以一次分配一块足够大的内存,供 Foo 和 ref_count 对象容身。
在这里插入图片描述

但是注意,不能将一个原始指针直接赋值给一个智能指针,例如,下面这种方法是错误的:

std::shared_ptr<int> p = new int(1);

正确的操作是通过构造函数和辅助方法来初始化

std::shared_ptr<int> p;
p1.reset(new int(1));

例子:

#include <iostream>
#include <memory>
using namespace std;int main()
{std::shared_ptr<int> p1;p1.reset(new int(1));std::shared_ptr<int> p2 = p1;// 引用计数此时应该是2cout << "p2.use_count() = " << p2.use_count() << endl;p1.reset();cout << "p1.reset()\n";// 引用计数此时应该是2cout << "p2.use_count()= " << p2.use_count() << endl;if (!p1){cout << "p1 is empty\n";}if (!p2){cout << "p2 is empty\n";}p2.reset();cout << "p2.reset()\n";cout << "p2.use_count()= " << p2.use_count() << endl;if (!p2){cout << "p2 is empty\n";}return 0;
}

结果是

p2.use_count() = 2
p1.reset()
p2.use_count()= 1
p1 is empty
p2.reset()
p2.use_count()= 0
p2 is empty

2.4 获取原始指针get

当需要获取原始指针时,可以通过get方法来返回原始指针,代码如下所示:

std::shared_ptr<int> ptr(new int(1));
int *p = ptr.get();

谨慎使用p.get()的返回值,因为很容易出错且难以排查。

p.get()的返回值就相当于一个裸指针的值,不合适的使用这个值,上述陷阱的所有错误都有可能发生,遵守以下几个约定:

1)不要保存p.get()的返回值 ,无论是保存为裸指针还是shared_ptr都是错误的。
若保存为裸指针不知什么时候就会变成空悬指针,若保存为shared_ptr则产生了独立指针

2)不要delete p.get()的返回值 ,会导致对一块内存delete两次的错误。

2.5 指定删除器

如果用shared_ptr管理非new对象或是没有析构函数的类时,应当为其传递合适的删除器。

#include <iostream>
#include <memory>
using namespace std;
void DeleteIntPtr(int *p) {cout << "call DeleteIntPtr" << endl;delete p;
}
int main()
{std::shared_ptr<int> p(new int(1), DeleteIntPtr);return 0;
}

当p的引用计数为0时,自动调用删除器DeleteIntPtr来释放对象的内存。删除器可以是一个lambda表达式,上面的写法可以改为:

std::shared_ptr<int> p(new int(1), [](int *p) {cout << "call lambda delete p" << endl;delete p;});

特别的是,当我们用shared_ptr管理动态数组时,需要指定删除器,因为shared_ptr的默认删除器不支持数组对象,代码如下所示:

std::shared_ptr<int> p(new int[10], [](int *p) { delete [] p;});

2.6 使用shared_ptr要注意的问题

1、不要用一个原始指针初始化多个shared_ptr
例如下面错误范例:

int *ptr = new int;
shared_ptr<int> p1(ptr);
shared_ptr<int> p2(ptr); // 逻辑错误

在这里插入图片描述

2、不要在函数实参中创建shared_ptr
对于下面的写法:

function(shared_ptr<int>(new int), g()); //有缺陷

因为C++的函数参数的计算顺序在不同的编译器不同的约定下可能是不一样的,一般是从右到左,但也可能从左到右,所以,可能的过程是先new int,然后调用g(),如果恰好g()发生异常,而shared_ptr还没有创建, 则int内存泄漏了,正确的写法应该是先创建智能指针,代码如下:

shared_ptr<int> p(new int);
function(p, g());

3、通过shared_from_this()返回this指针
不要将this指针作为shared_ptr返回出来,因为this指针本质上是一个裸指针,因此,这样可能会导致重复析构,看下面的例子。

#include <iostream>
#include <memory>
using namespace std;
class A
{
public:shared_ptr<A> GetSelf(){return shared_ptr<A>(this); // 不要这么做}A(){cout<<"Construction A"<<endl;}~A(){cout << "Destructor A:"<<this << endl;}
};
int main()
{shared_ptr<A> sp1(new A);shared_ptr<A> sp2 = sp1->GetSelf();cout<<"sp1.use_count() = "<<sp1.use_count()<<endl;cout<<"sp2.use_count() = "<<sp2.use_count()<<endl;return 0;
}

运行后调用了两次析构函数。

Construction A
sp1.use_count() = 1
sp2.use_count() = 1
Destructor A:0x1024000
Destructor A:0x1024000

在这个例子中,由于用同一个指针(this) 构造了两个智能指针sp1和sp2,而他们之间是没有任何关系的,即指向各自的 control block,但是指向对象都是new int。因此 在离开作用域之后this将会被构造的两个智能指针各自析构,导致重复析构的错误。

正确返回this的shared_ptr的做法是:让目标类通过std::enable_shared_from_this类,然后使用基类的成员函数shared_from_this()来返回this的shared_ptr,如下所示。

#include <iostream>
#include <memory>
using namespace std;class A: public std::enable_shared_from_this<A>
{
public:shared_ptr<A>GetSelf(){return shared_from_this(); //}A(){cout<<"Construction A"<<endl;}~A(){cout << "Destructor A" << endl;}
};
int main()
{shared_ptr<A> sp1(new A);shared_ptr<A> sp2 = sp1->GetSelf();  // okcout<<"sp1.use_count() = "<<sp1.use_count()<<endl;cout<<"sp2.use_count() = "<<sp2.use_count()<<endl;return 0;
}

结果是

Construction A
sp1.use_count() = 2
sp2.use_count() = 2
Destructor A

4、避免循环引用
循环引用会导致内存泄漏

#include <iostream>
#include <memory>
using namespace std;
class A;
class B;
class A {
public:std::shared_ptr<B> bptr;~A() {cout << "A is deleted" << endl;}
};
class B {
public:std::shared_ptr<A> aptr;~B() {cout << "B is deleted" << endl;}
};
int main()
{{std::shared_ptr<A> ap(new A);std::shared_ptr<B> bp(new B);ap->bptr = bp;bp->aptr = ap;}cout<< "main leave" << endl;  // 循环引用导致ap bp退出了作用域都没有析构return 0;
}

循环引用导致ap和bp的引用计数为2,在离开作用域之后,ap和bp的引用计数减为1,并不回减为0,导致两个指针都不会被析构,产生内存泄漏。

解决的办法是把A和B任何一个成员变量改为weak_ptr,具体方法见weak_ptr章节。

三、unique_ptr独占的智能指针

3.1 unique_ptr是一个独占型的智能指针

unique_ptr是一个独占型的智能指针,它不允许其他的智能指针共享其内部的指针,不允许通过赋值将
一个unique_ptr赋值给另一个unique_ptr。下面的错误示例。

unique_ptr<T> my_ptr(new T);
unique_ptr<T> my_other_ptr = my_ptr;  // 报错,不能复制

unique_ptr不允许复制,但可以通过函数返回给其他的unique_ptr,还可以通过·std::move·来转移到其他的unique_ptr,这样它本身就不再拥有原来指针的所有权了。例如

unique_ptr<T> my_ptr(new T); // 正确
unique_ptr<T> my_other_ptr = std::move(my_ptr); // 正确
unique_ptr<T> ptr = my_ptr;  // 报错,不能复制

std::make_shared是c++11的一部分,但std::make_unique不是。它是在c++14里加入标准库的。

auto upw1(std::make_unique<Widget>());   // with make func
std::unique_ptr<Widget> upw2(new Widget); // without make func

3.2 unique_ptr可以指向一个数组

std::unique_ptr<int []> ptr(new int[10]);
ptr[9] = 9;
std::shared_ptr<int []> ptr2(new int[10]);  // 这个是不合法的

3.3 unique_ptr需要确定删除器的类型

unique_ptr需要确定删除器的类型,所以不能像shared_ptr那样直接指定删除器

std::shared_ptr<int> ptr3(new int(1), [](int *p){delete  p;}); // 正确
std::unique_ptr<int> ptr4(new int(1), [](int *p){delete  p;}); // 错误

关于shared_ptrunique_ptr的使用场景是要根据实际应用需求来选择。

如果希望只有一个智能指针管理资源或者管理数组就用unique_ptr,如果希望多个智能指针管理同一个资源就用shared_ptr

四、weak_ptr弱引用的智能指针

shared_ptr虽然已经很好用了,但是有一点shared_ptr智能指针还是有内存泄露的情况,当两个对象相互
使用一个shared_ptr成员变量指向对方,会造成循环引用,使引用计数失效,从而导致内存泄漏。

weak_ptr 是一种不控制对象生命周期的智能指针, 它指向一个 shared_ptr 管理的对象. 进行该对象的内存管理的是那个强引用的shared_ptrweak_ptr 只是提供了对管理对象的一个访问手段。

weak_ptr 设计的目的是为配合 shared_ptr而引入的一种智能指针来协助 shared_ptr工作, 它只可以从一个 shared_ptr或另一个 weak_ptr 对象构造, 它的构造和析构不会引起引用记数的增加或减少。

4.1 基本用法

1)通过use_count()方法获取当前观察资源的引用计数,如下所示:

shared_ptr<int> sp(new int(10));
weak_ptr<int> wp(sp);
cout << wp.use_count() << endl;  //结果讲输出1

2)通过expired()方法判断所观察资源是否已经释放,如下所示

shared_ptr<int> sp(new int(10));
weak_ptr<int> wp(sp);
if(wp.expired())cout << "weak_ptr无效,资源已释放";
elsecout << "weak_ptr有效";

3)通过lock方法获取监视的shared_ptr

#include <iostream>
#include <memory>
using namespace std;std::weak_ptr<int> gw;
void f()
{auto spt = gw.lock();		if(gw.expired()) {cout << "gw无效,资源已释放";}else {cout << "gw有效, *spt = " << *spt << endl;}
}
int main()
{{auto sp  = std::make_shared<int>(42);gw = sp;f();}f();
return 0;
}

结果是

gw有效, *spt = 42
gw无效,资源已释放

4.2 返回this指针

shared_ptr章节中提到不能直接将this指针返回shared_ptr,需要通过派生std::enable_shared_from_this类,并通过其方法shared_from_this来返回指针.
原因是std::enable_shared_from_this类中有一个weak_ptr,这个weak_ptr用来观察this智能指针。调用shared_from_this()方法,会调用内部这个weak_ptrlock()方法,将所观察的shared_ptr返回

#include <iostream>
#include <memory>
using namespace std;class A: public std::enable_shared_from_this<A>
{
public:shared_ptr<A>GetSelf(){return shared_from_this(); //}A(){cout<<"Construction A"<<endl;}~A(){cout << "Destructor A" << endl;}
};
int main()
{shared_ptr<A> sp1(new A);shared_ptr<A> sp2 = sp1->GetSelf();  // okcout<<"sp1.use_count() = "<<sp1.use_count()<<endl;cout<<"sp2.use_count() = "<<sp2.use_count()<<endl;return 0;
}

结果是

Construction A
sp1.use_count() = 2
sp2.use_count() = 2
Destructor A

在外面创建A对象的智能指针和通过对象返回this的智能指针都是安全的,因为shared_from_this()是内部的weak_ptr调用lock()方法之后返回的智能指针,在离开作用域之后,sp1的引用计数减为0,A对象会被析构,不会出现A对象被析构两次的问题。

需要注意的是,获取自身智能指针的函数尽在shared_ptr的构造函数被调用之后才能使用,因为enable_shared_from_this内部的weak_ptr只有通过shared_ptr才能构造。

需要注意的是,获取自身智能指针的函数尽在shared_ptr的构造函数被调用之后才能使用,因为enable_shared_from_this内部的weak_ptr只有通过shared_ptr才能构造。

4.3 解决循环引用问题

shared_ptr章节提到智能指针循环引用的问题,因为智能指针的循环引用会导致内存泄漏,可以通过weak_ptr解决该问题,只要将A或B的任意一个成员变量改为weak_ptr

#include <iostream>
#include <memory>
using namespace std;
class A;
class B;
class A {
public:std::weak_ptr<B> bptr;~A() {cout << "A is deleted" << endl;}
};
class B {
public:std::shared_ptr<A> aptr;~B() {cout << "B is deleted" << endl;}
};
int main()
{{std::shared_ptr<A> ap(new A);cout<< "ap.use_count():"<< ap.use_count()<<endl;std::shared_ptr<B> bp(new B);cout<< "bp.use_count():"<<bp.use_count()<<endl;cout<<"===================="<<endl;ap->bptr = bp;cout<< "ap.use_count():"<<ap.use_count()<<endl;cout<< "bp.use_count():"<<bp.use_count()<<endl;cout<<"===================="<<endl;bp->aptr = ap;cout<< "ap.use_count():"<<ap.use_count()<<endl;cout<< "bp.use_count():"<<bp.use_count()<<endl;}cout<< "main leave" << endl;  // 循环引用导致ap bp退出了作用域都没有析构return 0;
}
ap.use_count():1
bp.use_count():1
====================
ap.use_count():1
bp.use_count():1
====================
ap.use_count():2
bp.use_count():1
B is deleted
A is deleted
main leave

这样在对B的成员赋值时,即执行ap->bptr = bp;;时,由于bptrweak_ptr,它并不会增加引用计数,所以bp的引用计数仍然会是1。
在离开作用域之后,bp的引用计数为减为0,B指针会被析构,析构后其内部的aptr的引用计数会被减为1,然后在离开作用域后ap引用计数又从1减为0,A对象也被析构,不会发生内存泄漏.

4.4 使用注意事项

1、weak_ptr在使用前需要检查合法性。

weak_ptr<int> wp;
{shared_ptr<int>  sp(new int(1));  //sp.use_count()==1wp = sp; //wp不会改变引用计数,所以sp.use_count()==1shared_ptr<int> sp_ok = wp.lock(); //wp没有重载->操作符。只能这样取所指向的对象
}
shared_ptr<int> sp_null = wp.lock(); //sp_null .use_count()==0;

因为上述代码中spsp_ok离开了作用域,其容纳的对象已经被释放了,得到了一个容纳NULL指针的sp_null对象。

在使用wp前需要调用wp.expired()函数判断一下。因为wp还仍旧存在,虽然引用计数等于0,仍有某处“全局”性的存储块保存着这个计数信息。直到最后一个weak_ptr对象被析构,这块“堆”存储块才能被回收。否则weak_ptr无法指导自己所容纳的那个指针资源的当前状态。

weak_ptr<int> wp;
shared_ptr<int> sp_ok;
{shared_ptr<int>  sp(new int(1));  //sp.use_count()==1wp = sp; //wp不会改变引用计数,所以sp.use_count()==1sp_ok = wp.lock(); //wp没有重载->操作符。只能这样取所指向的对象
}
// 在这个作用域结束后,sp 和其所管理的内存将会被销毁,因为 sp 的引用计数变为 0,触发内存释放。
if(wp.expired()) {cout << "shared_ptr is destroy" << endl;
} else {cout << "shared_ptr no destroy" << endl;
}
shared_ptr no destroy

在作用域内,创建了一个新的shared_ptr对象sp并将其所有权转交给wp,然后通过调用wp.lock()方法从wp中获取了shared_ptr的所有权。由于wp仍然有效,所以输出结果为"shared_ptr no destroy"。当作用域结束时,sp所管理的int对象会被销毁,但这并不影响wp的有效性。

五、为什么多线程读写 shared_ptr 要加锁?

为什么多线程读写 shared_ptr 要加锁?

本专栏知识点是通过<零声教育>的系统学习,进行梳理总结写下文章,对c/c++linux课程感兴趣的读者,可以点击链接,详细查看详细的服务器课程

相关文章:

C++新特性:智能指针

一 、为什么需要智能指针 智能指针主要解决以下问题&#xff1a; 1&#xff09;内存泄漏&#xff1a;内存手动释放&#xff0c;使用智能指针可以自动释放 2&#xff09;共享所有权指针的传播和释放&#xff0c;比如多线程使用同一个对象时析构问题&#xff0c;例如同样的数据…...

SAP FI之批量修改财务凭证的BAPI

文章目录 前言一、pandas是什么&#xff1f;二、使用步骤 1.引入库2.读入数据总结 前言 一般涉及修改财务凭证&#xff0c;或者其它凭证&#xff0c;不应直接更新数据库&#xff0c;而是使用系统提供的function module,或者BAPI&#xff0c;或者使用BDC。 一、 示例&#xf…...

Spring Boot + Vue的网上商城之商品分类

Spring Boot Vue的网上商城之商品分类 在网上商城中&#xff0c;商品分类是非常重要的一个功能&#xff0c;它可以帮助用户更方便地浏览和筛选商品。本文将介绍如何使用Spring Boot和Vue来实现商品分类的功能&#xff0c;包括一级分类和二级分类的管理以及前台按分类浏览商品…...

Docker 容器逃逸漏洞 (CVE-2020-15257)复现

漏洞概述 containerd是行业标准的容器运行时&#xff0c;可作为Linux和Windows的守护程序使用。在版本1.3.9和1.4.3之前的容器中&#xff0c;容器填充的API不正确地暴露给主机网络容器。填充程序的API套接字的访问控制验证了连接过程的有效UID为0&#xff0c;但没有以其他方式…...

Python 如何使用 csv、openpyxl 库进行读写 Excel 文件详细教程(更新中)

csv 基本概述 首先介绍下 csv (comma separated values)&#xff0c;即逗号分隔值&#xff08;也称字符分隔值&#xff0c;因为分隔符可以不是逗号&#xff09;&#xff0c;是一种常用的文本格式&#xff0c;用以存储表格数据&#xff0c;包括数字或者字符。 程序在处理数据时…...

$nextTick属性使用与介绍

属性介绍 $nextTick 是 Vue.js 中的一个重要方法&#xff0c;之前我们也说过$ref 等一些重要的属性&#xff0c;这次我们说$nextTick&#xff0c;$nextTick用于在 DOM 更新后执行回调函数。它通常用于处理 DOM 更新后的操作&#xff0c;因为 Vue 在更新 DOM 后不会立即触发回调…...

【群智能算法改进】一种改进的鹈鹕优化算法 IPOA算法[2]【Matlab代码#58】

文章目录 【获取资源请见文章第5节&#xff1a;资源获取】1. 原始POA算法2. 改进后的IPOA算法2.1 随机对立学习种群初始化2.2 动态权重系数2.3 透镜成像折射方向学习 3. 部分代码展示4. 仿真结果展示5. 资源获取 【获取资源请见文章第5节&#xff1a;资源获取】 1. 原始POA算法…...

k8s 入门到实战--部署应用到 k8s

k8s 入门到实战 01.png 本文提供视频版&#xff1a; 背景 最近这这段时间更新了一些 k8s 相关的博客和视频&#xff0c;也收到了一些反馈&#xff1b;大概分为这几类&#xff1a; 公司已经经历过服务化改造了&#xff0c;但还未接触过云原生。公司部分应用进行了云原生改造&…...

编程语言新特性:instanceof的改进

以前也写过类似的博文&#xff0c;可能重复。 要判断一个对象是哪个类或父类的实例&#xff0c;JAVA用到instanceof&#xff0c;其实语言也有类似语法。而类一般是多层继承的&#xff0c;有时就让人糊涂。所以我提出改进思路&#xff1a; instanceof&#xff1a;保持不变。ins…...

数据挖掘的学习路径

⭐️⭐️⭐️⭐️⭐️欢迎来到我的博客⭐️⭐️⭐️⭐️⭐️ &#x1f434;作者&#xff1a;秋无之地 &#x1f434;简介&#xff1a;CSDN爬虫、后端、大数据领域创作者。目前从事python爬虫、后端和大数据等相关工作&#xff0c;主要擅长领域有&#xff1a;爬虫、后端、大数据…...

逻辑回归Logistic

回归 概念 假设现在有一些数据点&#xff0c;我们用一条直线对这些点进行拟合&#xff08;这条直线称为最佳拟合直线&#xff09;&#xff0c;这个拟合的过程就叫做回归。进而可以得到对这些点的拟合直线方程。 最后结果用sigmoid函数输出 因此&#xff0c;为了实现 Logisti…...

Flink提交jar出现错误RestHandlerException: No jobs included in application.

今天打包一个flink的maven工程为jar&#xff0c;通过flink webUI提交&#xff0c;发现居然报错。 如上图所示&#xff0c;提示错误为&#xff1a; Server Response Message: org.apache.flink.runtime.rest.handler.RestHandlerException: No jobs included in application. …...

【数仓基础(一)】基础概念:数据仓库【用于决策的数据集合】的概念、建立数据仓库的原因与好处

文章目录 一. 数据仓库的概念1. 面向主题2. 集成3. 随时间变化4. 非易失粒度 二. 建立数据仓库的原因三. 使用数据仓库的好处 一. 数据仓库的概念 数据仓库的主要作用&#xff1a; 数据仓库概念主要是解决多重数据复制带来的高成本问题。 在没有数据仓库的时代&#xff0c;需…...

电商类面试问题--01Elasticsearch与Mysql数据同步问题

在实现基于关键字的搜索时&#xff0c;首先需要确保MySQL数据库和ES库中的数据是同步的。为了解决这个问题&#xff0c;可以考虑两层方案。 全量同步&#xff1a;全量同步是在服务初始化阶段将MySQL中的数据与ES库中的数据进行全量同步。可以在服务启动时&#xff0c;对ES库进…...

天线材质介绍--FPC天线

...

vue3 的 ref、 toRef 、 toRefs

1、ref: 对原始数据进行拷贝。当修改 ref 响应式数据的时候&#xff0c;模版中引用 ref 响应式数据的视图处会发生改变&#xff0c;但原始数据不会发生改变 <template><div>{{refA}}</div> </template><script lang"ts" setup> impor…...

WebRTC中 setup:actpass、active、passive

1、先看一下整个DTLS的流程 setup:actpass、active、passive就发生在Offer sdp和Anser SDP中 Offer的SDP是setup:actpass,这个是服务方&#xff1a; v0\r o- 1478416022679383738 2 IN IP4 127.0.0.1\r s-\r t0 0\r agroup:BUNDLE 0 1\r aextmap-allow-mixed\r amsid-semanti…...

ModuleNotFoundError: No module named ‘lavis‘解决方案

大家好,我是爱编程的喵喵。双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。现为CSDN博客专家、人工智能领域优质创作者。喜欢通过博客创作的方式对所学的…...

双指针的问题解法以及常见的leetcode例题。

目录 介绍&#xff1a; 问题1&#xff1a;双指针 剑指offer57 和为S的两个数字。 问题2&#xff1a;剑指Offer 21. 调整数组顺序使奇数位于偶数前面 问题3&#xff1a;连续奇数子串&#xff08;笔试遇到的真题&#xff09; 问题4&#xff1a;滑动窗口的最大值 介绍&#…...

python容器模块Collections

Python附带一个模块&#xff0c;它包含许多容器数据类型&#xff0c;名字叫作collections defaultdict defaultdict与dict类型不同&#xff0c;你不需要检查key是否存在&#xff0c;所以我们能这样做&#xff1a; from collections import defaultdict colours ((Yasoob, Y…...

排序算法学习记录-快速排序

快速排序 快速排序关键在于确定一个中间值&#xff0c;使得小于这个中间值的数在左边&#xff0c;大于这个中间值的数在右边。那么中间值该如何确定呢&#xff1f;有以下几种做法 首元素&#xff0c;也就是arr[l]尾元素&#xff0c;也就是arr[r]中间元素&#xff0c;也就是ar…...

安装windows版本的ros2 humble的时候,最后报错

"[rti_connext_dds_cmake_module][warning] RTI Connext DDS environment script not found (\resource\scripts\rtisetenv_x64Win64VS2017.bat). RTI Connext DDS will not be available at runtime, unless you already configured PATH manually." 意思是没找到。…...

Nginx 学习(十)高可用中间件的配置与实现

一 Keepalived热备 1 概述 调度器出现单点故障&#xff0c;如何解决?Keepalived实现了高可用集群Keepalived最初是为LVS设计的&#xff0c;专门监控各服务器节点的状态Keepalived后来加入了VRRP功能&#xff0c;防止单点故障 2 运行原理 Keepalived检测每个服务器节点状…...

[刷题记录]牛客面试笔刷TOP101

牛客笔试算法必刷TOP101系列,每日更新中~ 1.合并有序链表2023.9.3 合并两个排序的链表_牛客题霸_牛客网 (nowcoder.com) 题意大致为: 将两个链表中的元素按照从小到大的顺序合并成为一个链表. 所给予的条件: 给出的所要合并的链表都是从小到大顺序排列的. 思路: 创建一…...

降水预报之双重惩罚

在降水预报中&#xff0c;通常会出现 "双重惩罚问题 "的指标或度量包括那些常用于预报验证的指标或度量。当假阴性&#xff08;漏报降水事件&#xff09;和假阳性&#xff08;误报&#xff09;受到同等惩罚或加权时&#xff0c;就会出现双重惩罚问题&#xff0c;这在…...

一条SQL语句的执行过程(附一次两段式提交)

一条SQL语句的完整执行过程是怎样的呢&#xff1f;我们用select和update语句来举例。 注意在mysql8后&#xff0c;进入服务层后&#xff0c;取消了去查询缓存(属于Server服务层)这个步骤&#xff0c;缓存中key是SQL语句&#xff0c;value是值&#xff0c;这样其实并不会提升性…...

Python基础知识详解:数据类型、对象结构、运算符完整分析

文章目录 python基础知识数据类型类型检查对象&#xff08;object&#xff09;对象的结构变量和对象类型转换运算符(操作符)1. 算术运算符2. 赋值运算符3. 比较运算符&#xff08;关系运算符&#xff09;4. 逻辑运算符5. 条件运算符&#xff08;三元运算符&#xff09; 总结 py…...

基于Streamlit的应用如何通过streamlit-authenticator组件实现用户验证与隔离

Streamlit框架中默认是没有提供用户验证组件的&#xff0c;大家在基于streamlit快速实现web应用服务过程中&#xff0c;不可避免的需要配置该应用的访问范围和权限&#xff0c;即用户群体&#xff0c;一般的做法有两种&#xff0c;一种是通过用户密码验证机制&#xff0c;要求只…...

[虚幻引擎插件介绍] DTGlobalEvent 蓝图全局事件, Actor, UMG 相互回调,自由回调通知事件函数,支持自定义参数。

本插件可以在虚幻的蓝图 Actor&#xff0c; Obiect&#xff0c;UMG 里面指定绑定和执行消息&#xff0c;可带自定义参数。 参数支持 Bool&#xff0c;Byte&#xff0c;Int&#xff0c;Int64&#xff0c;Float&#xff0c;Name&#xff0c;String&#xff0c;Text&#xff0c;Ve…...

2023数学建模国赛选题建议及BC题思路

大家好呀&#xff0c;全国大学生数学建模竞赛今天下午开赛啦&#xff0c;在这里先带来初步的选题建议及思路。 目前团队正在写B题和C题完整论文&#xff0c;后续还会持续更新哈&#xff0c;以下只是比较简略的图文版讲解&#xff0c;团队目前正在写B、C题完整论文&#xff0c;…...

wordpress 关闭摘要/百度浏览器官网下载并安装

CAD看图王(CAD手机看图专业版)集快速看图、DWG画图、CAD批注、制图于一身的CAD看图软件&#xff0c;全球累计免费用户超过5000万。支持AutoCAD、浩辰CAD、天正建筑、酷家乐等国内外CAD图纸格式&#xff0c;图纸原生显示CAD图纸不失真。专业的fonts字体解析告别文字乱码&#xf…...

西地那非片的正确服用方法/百度关键词seo

触发器、存储过程和函数三者有何区别&#xff1f; 回复&#xff1a;触发器、存储过程和函数三者有何区别&#xff1f; 触发器是特殊的存储过程&#xff0c;存储过程需要程序调用&#xff0c;而触发器会自动执行&#xff1b;你所说的函数是自定义函数吧&#xff0c;函数是根据输…...

怎样做货源网站/创新驱动发展战略

滚动穿透问题 详情描述 解决方法&#xff1a; 1. 在弹层出现时给body设置position&#xff1a;fixed&#xff0c;top&#xff1a;-滚动条高度&#xff1b;弹层消失的时候获取body的top&#xff0c;$(window).scrollTop(-body的高度)。解决大部分场景&#xff0c;在ios微信浏…...

红包打赏的网站怎么做/百度一下下载

Controllerpackage com.controller;import com.BaseController; import com.entity.Catalog; import com.entity....

网站做301重定向的作用/网络广告投放

主要讲一下在Linux下安装tensorflow gpu版的两种方式&#xff1a;使用conda和pip安装。安装cpu版本比较简单&#xff0c;安装命令相同&#xff0c;但是省去了安装cuda和cudnn。 注意&#xff1a; 在安装tensorflow gpu版本前&#xff0c;首先要清楚不同版本tensorflow对应不同…...

河南手机网站建设多少钱/济南网络推广公司电话

有关枚举我用例子来进行解释说明 一. enum Sex//性别 {MALE,FEMALE,SECRET }; enum Color//颜色 {RED,GREEN,BLUE }; int main() {enum Sex s MALE;enum Color c RED;return 0; }个人理解&#xff1a; enum Sex s &#xff1b;后面只能跟 MALE,FEMALE,SECRET 防止自己出错。…...