RAII与智能指针
RAII与智能指针
- 1.RAII
- 1.1RAII理解
- 1.2RAII的原理
- 1.2.1简单的例子说明局部对象的自动销毁的特性
- 1.2.2 RAII 过程
- 2.智能指针
- 2.1 auto_ptr
- 2.1.1auto_ptr的使用
- 构造函数与析构函数
- 拷贝构造函数与赋值
- 提领操作
- auto ptr其它函数
- 2.1.2autoptr使用的注意事项
- 2.2 unique_ptr
- 2.2.1unique_ptr的使用
- unique_ptr的创建
- unique_ptr不能进行拷贝构造和赋值操作
- unique_ptr可以进行移动构造和移动赋值操作
- unique_ptr虽然没有拷贝操作,但是可以从函数中返回unique_ptr
- 2.2.2unique_ptr使用场景
- 2.3 shared_ptr(共享指针)
- 2.3.1 shared_ptr的使用
- shared_ptr的创建
- shared_ptr的访问
- shared_ptr的拷贝和赋值操作
- shared_ptr的引用计数
- 2.3.2 shared_ptr的线程安全
- 2.3.3 shared_ptr与unordered map使用
- 2.4 weak_ptr
- 2.4.1 weak_ptr理解
- 2.4.2 为什么会有weak_ptr
- 2.3.3 weak_ptr使用
- 创建weak_ptr实例
- 判断weak_ptr指向对象是否存在
- weak_ptr使用
- 2.5 几种智能指针的比较
1.RAII
1.1RAII理解
RAII(Resource Acquisition ls lnitialization) ,资源获取即初始化,是由 c++之父 Biarne Stroustrup 提出的。
使用局部对象来管理资源的技术称为资源获取即初始化,这里的资源主要是指操作系统中有限的东西如内存、网络套接字,互斥量,文件句柄等等,局部对象是指存储在栈的对象,它的生命周期是由操作系统来管理的,无需人工介入。
1.2RAII的原理
资源的使用一般经历三个步骤:
- 获取资源(创建对象),
- 使用资源,
- 销毁资源 (析构对象)。
但是资源的销毁往往是程序员经常忘记的一个环节,所以程序界就想如何在程序员中让资源自动销毁呢?解决问题的方案是: RAIl,它充分的利用了 C++语言局部对象自动销毁的特性来控制资源的生命周期。
1.2.1简单的例子说明局部对象的自动销毁的特性
#include <iostream>
#include <string>using namespace std;class Student
{
private:const string s_name;int s_age;
public:Student(const string name = "",int age = 0):s_name(name),s_age(age){cout<<"Construct a Student"<<endl;}~Student(){cout<<"Destory a Student"<<endl;}
};int main(){Student stu1; #局部对象return 0;
}
从 Student类可以看出,当我们在 main 函数中声明一个局部对象的时候,会自动调用构造函数进行对象的初始化,当整个 main 函数执行完成后,自动调用析构函数来销毁对象,整个过程无需人工介入,由操作系统自动完成。
于是,基于上述实现方式,可以想到,当我们在使用资源的时候,在构造函数中进行初始化,在析构函数中进行销毁。
1.2.2 RAII 过程
- 设计一个类封装资源
- 在构造函数中初始化
- 在析构函数中执行销毁操作
- 使用时声明一个该类的对象
2.智能指针
智能指针其实是将指针进行了封装,可以像普通指针一样进行使用,同时可以自行进行释放,避免忘记释放指针指向的内存地址造成内存泄漏。
C11 里面的四个智能指针:
- auto ptr,
- unique ptr,
- shared ptr,
- weak ptr
下面对这四个智能指针进行一一介绍:
2.1 auto_ptr
auto_ptr是较早版本的智能指针,已经被 C11 弃用,C98 中 auto_ptr 所做的事情,就是动态分配对象以及当对象不再需要时自动执行清理。
2.1.1auto_ptr的使用
构造函数与析构函数
auto_ptr 在构造时获取对某个对象的所有权(ownership),在析构时释放该对象,提高代码安全性,因为我们不必关系应该如何释放auto ptr,也不用担心发生异常时会有内存泄漏。
int *p = new int(10);auto_ptr<int> ap(p);
注意点:
- auto ptr 析构的时候会删除他所拥有的那个对象,所以我们要注意两个auto_ptr 不能同时拥有同一个对象。
int *p = new int(10);auto_ptr<int> ap1(p);
auto_ptr<int> ap2(p);
因为 ap1 与 ap2 都认为指针 p 是归它管的,在析构时都试图删除 p,两次删除同一个对象的行为在C++标准中是未定义的。所以我们必须防止这样使用 auto ptr
- 不应该用 auto ptr 来管理一个数组指针
string * sar = new string[10];auto_ptr<string> ap3(sar);
- 构造函数的 explicit 关键词有效阻止从一个“裸指针隐式转换成 auto ptr 类型
拷贝构造函数与赋值
auto ptr 要求其对“裸”指针的完全占有性。也就是说一个“裸”指针不能同时被两个以上的 auto ptr 所拥有。那么,在拷贝构造或赋值操作时,就必须作特殊的处理来保证这个特性。
auto ptr的做法是“所有权转移”,即拷贝或赋值的源对象将失去对“裸”指针的所有权,所以,与一般拷贝构造函数,赋值承数不同,auto ptr 的拷贝构造函数,赋值函数的参数为引用而不是常引用(const reference).
当然,一个 auto ptr 也不能同时拥有两个以上的“裸”指针,所以,拷贝或赋值的目标指针将先释放其所拥有的对象,然后管理旧指针的资源,因此旧指针即指向nullptr,在访问旧指针时会出现悬空现象。
注意点:
- auto ptr 被拷贝或被赋值后,其已经失去对原对象的所有权,这个时候,对这个 auto ptr 的提领(dereference)操作是不安全的。
int *p = new int(10);
auto_ptr<int> ap1(p);
auto_ptr<int> ap2 = ap1;cout<<*ap1<<endl; //error,此时ap1已经失去对p指针的拥有权
- 将 auto_ptr 作为函数参数按值传递时,函数调用过程中在函数的作用域中会产生一个局部对象来接收传入的 auto ptr(拷贝构造),这样,传入的实参 auto ptr 就失去了其对原对象的所有权,而该对象会在函数退出时被局部 auto ptr 删除。
void fun(auto_ptr<int> ap)
{cout<< *ap<< endl;
}int main(){auto_ptr<int> ap1(new int(10));fun(ap1);cout<<*ap1<<endl; //error,经过fun(ap1)函数调用,ap1已经不再拥有任何对象了return 0;
}
这种情况容易出错,所以 auto ptr 作为函数参数按值传递是一定要避免的。或许用auto ptr 的指针或引用作为函数参数或许可以,但是我们并不知道在函数中对传入的 auto ptr 做了什么,如果当中某些操作使其失去了对对象的所有权,那么这还是可能会导致致命的执行期错误。也许用 const reference 的形式来传递 auto ptr 会是一个不错的选择。
- auto ptr在基类和子类隐式转换
class Object{ };class Base: public Object{ };auto_ptr<Object> apobj = auto_ptr<Base>(new Base);
提领操作
提领操作有两个操作:
- 返回其所拥有的对象的引用,
- 实现了通过 auto ptr 调用其所拥有的对象的成员(首先要确保这个智能指针确实拥有某个对象,否则,这个操作的行为即对空指针的提领是未定义)。
auto ptr其它函数
- get 用来显式的返回 auto ptr 所拥有的对象指针。我们可以发现,标准库提供的 auto ptr 既不提供从裸”指针到auto _ptr 的隐式转换(构造函数为 explicit),也不提供 auto ptr 到指针的隐式转换,从使用上来讲可能不那么的灵活,考虑到其所带来的安全性还是值得的。
- release,用来转移所有权。
- reset,用来接收所有权,如果接收所有权的 auto ptr 如果已经拥有某对象,必须先释放该对象.
2.1.2autoptr使用的注意事项
- auto ptr 不能指向数组
- auto_ptr 不能共享所有权
- auto_ptr 不能通过复制操作来初始化
- auto_ptr 不能放入容器中使用
- auto ptr 不能作为容器的成员
2.2 unique_ptr
C11 中使用 unique_ptr 替代auto_ptr
unique 是独特的、唯一的意思,故名思议,unique ptr 可以“独占”地拥有它所指向的对象,是一种定义在<memory中的智能指针(smart pointer),保证一个对象同一时间只有一个智能指针。
unique_ptr 对象中保存指向某个对象的指针,当它本身被删除或者离开其作用域时会自动释放其指向对象所占用的资源。
2.2.1unique_ptr的使用
unique_ptr的创建
要想创建一个 unique ptr,需要将一个 new 操作符返回的指针传递给 unique ptr 的构造函数.
int main()
{ unique_ptr<int> pt(new int(10));cout<< *pt<< endl; //10return 0;
}
unique_ptr不能进行拷贝构造和赋值操作
unique_ptr& operator=( const unique_ptr& ) = delete;
unique_ptr可以进行移动构造和移动赋值操作
unique ptr 虽然没有支持普通的拷贝和赋值操作,但却提供了一种移动机制来将指针的所有权从一个 unique ptr转移给另一个 unique ptr。如果需要转移所有权,可以使用 std:.move()函数
int main(){unique_ptr<int> pt1(new int(10));cout<< *pt1<< endl; //10unique_ptr<int> pt2(move(pt1)); cout<<"--------------"<<endl;//cout<< *pt1<< endl; //出错,为空cout<< *pt2<< endl; //10unique_ptr<int> pt3 = move(pt2); cout<<"~~~~~~~~~~~~~~~~"<<endl;//cout<< *pt1<< endl; //出错,为空//cout<< *pt2<< endl; //出错,为空cout<< *pt3<< endl; //10return 0;
}
unique_ptr虽然没有拷贝操作,但是可以从函数中返回unique_ptr
unique_ptr<int> clone(int a)
{unique_ptr<int> ptr(new int(a));return ptr;//返回unique_ptr
}int main()
{int val = 11;unique_ptr<int> pt1 = clone(val);cout<< *pt1<< endl; //11return 0;
}
2.2.2unique_ptr使用场景
- 为动态申请的资源提供异常安全保证
void fun_(int a)
{int *p = new int(5);//.....(抛出异常)
}void fun(int a)
{unique_ptr<int> ptr(new int(a));//.....(抛出异常)}
fun_是传统的写法:在动态申请内存后,有可能接下来的代码由于抛出异常或者提前退出 (if 语句)而没有执行 delete 操作。
解决的方法是使用 unique ptr 来管理动态内存,只要 unique ptr 指针创建成功,其析构函数都会被调用。确保动态资源被释放。
2. 返回函数内动态申请资源的所有权
unique_ptr<int> fun(int a)
{unique_ptr<int> ptr(new int(a));return ptr;//返回unique_ptr
}int main()
{int a = 12;unique_ptr<int> ret = fun(a);cout<< *ret<< endl; //12//函数结束后,自动释放资源return 0;
}
- 在容器中保存指针
int main()
{vector<unique_ptr<int>> vec;unique_ptr<int> pt(new int(5));vec.push_back(move(pt));for(int i = 0;i < vec.size();i++){cout<< *vec[i]<< endl; // 5}return 0;
}
- 管理动态数组
int main()
{ unique_ptr<vector<int>[]> pt(new vector<int> {1,2,3,4,5});for(int i = 0;i < pt.get()->size();i++){cout<<pt.get()->at(i)<<endl; //1,2,3,4,5}return 0;
}
2.3 shared_ptr(共享指针)
shared ptr 是一个引用计数的智能指针,用于共享对象的所有权。也就是说它允许多个指针指向同一个对象,并且维护了一个共享的引用计数器,当这个对象所有的智能指针被销毁时(引用计数器==0)就会自动进行回收。
class Object
{
private:int val;
public:Object(int x = 0):val(0){cout<< "Construct Object"<< endl;}~Object(){cout<< "Destory Object"<< endl;}};int main()
{shared_ptr<Object> pobj(new Object(13));//指针引用对象的个数cout<< " pobj:"<< pobj.use_count()<<endl; //1shared_ptr<Object> pobj1 = pobj;cout<< " pobj:"<< pobj.use_count()<<endl; //2cout<< " pobj1:"<< pobj1.use_count()<<endl; //2return 0;
}
一方面,跟 STL 中大多数容器类型一样,shared ptr 也是模板类,因此在创建 shared ptr 时需要指定其指向的类型。另一方面,shared ptr 指针允许让多个该类型的指针共享同一堆分配对象。同时 shared ptr 使用经典的“引用计数”方法来管理对象资源,每个 shared ptr 对象关联一个共享的引用计数。
2.3.1 shared_ptr的使用
shared_ptr的创建
- 调用 make shared 库函数,该函数会在堆中分配一个对象并初始化,最后返回指向此对象的share ptr 实例(安全、高效)
- 先 new 出一个对象,然后把其原始指针传递给 share ptr 的构造函数
shared_ptr<int> ptm = make_shared<int>(16);cout<< *ptm<< endl; //16shared_ptr<int> ptn(new int(15));cout<< *ptn<< endl; //15
shared_ptr的访问
- 解引用操作符*获得原始对象进而访问其各个成员,
- 指针访问符->来访问原始对象的各个成员。
shared_ptr的拷贝和赋值操作
对于 shared ptr 在拷贝和赋值时的行为是,每个 shared ptr 都有一个关联的计数值,通常称为引用计数。无论何时我们拷贝一个 shared ptr,计数器都会加1。当我们将一个指针的对象交给另一个指针管理后,其关联的引用计数就会减1。
例如,当用一个 shared ptr 初始化另一个 shred ptr时,或将它当做参数传递给一个函数以及作为函数的返回值时它所关联的计数器就会递增。
当我们给 shared ptr 赋予一个新值或是 shared ptr 被销(例如一个局部的 shared ptr 离开其作用域)时,计数器就会递减。
shared ptr 对象的计数器变为 0,它就会自动释放自己所管理的对象。
class Object
{
private:int val;
public:Object(int x = 0):val(0){cout<< "Construct Object"<< endl;}~Object(){cout<< "Destory Object"<< endl;}};int main()
{shared_ptr<Object> pobj = make_shared<Object>(13);//指针引用对象的个数cout<< " pobj:"<< pobj.use_count()<<endl; //1shared_ptr<Object> pobj1 = pobj;cout<< " pobj:"<< pobj.use_count()<<endl; //2cout<< " pobj1:"<< pobj1.use_count()<<endl; //2return 0;
}
对比我们上面的代码可以看到: 接下来,我们用 pob初始化 pobit1,两者关联的引用计数值增加为 2。随后,函数结束,pObi和 PObi2 相继离开函数作用域,相应的引用计数值分别自减 1 最后变为 0,于是 Obiect 对象被自动释放(调用其析构函数)。
shared_ptr的引用计数
shared _ptr 提供了两个函数来检查其共享的引用计数值,分别是
- unique()函数用来测试该 shared ptr 是否是原始指针唯一拥有者,也就是 use count()的返回值为 1时unique()返回 true,否则返回 false。
- use count()函数,该函数返回当前指针的引用计数值。值得注意的是 use count()函数能效率很低,应该只把它用于测试或调试。
2.3.2 shared_ptr的线程安全
- (shared_ptr) 的引用计数本身是线程安全(引用计数是原子操作)
- 多个线程同时读同一个 shared ptr 对象是线程安全的。
- 如果是多个线程对同一个 shared ptr 对象进行读和写,则需要加锁
- 多线程读写 shared_ptr 所指向的同一个对象,不管是相同的 shared ptr 对象,还是不同的 shared ptr 对象,也需要加锁保护。
2.3.3 shared_ptr与unordered map使用
如果把 shared_ptr 放到 unordered set 中,或者用于 unrdered map 的 key, 那么要小心 hash table 退化为链表。但是其 hash value 是 shared_ptr 隐式转换为 bool 的结果。也就是说,如果不自定义 hash 函数,那么unordered {set/map] 会退化为链表。
为什么要尽量使用 make shared()? 申请被管理对象以及引用计数的内存:调用适当的构造函数初始化对象,返回shared_ptr。
为了节省一次内存分配,原来 shared_ptr x(new Obiect (10) ; 需要为 Obiect对象 和 RefCnt 各分配次内存,现在用 make shared() 的话,可以一次分配一块足够大的内存,供 Obiect 和 RefCnt 对象容身。不过Obiect 的构造函数所需的参数要传给 make shared(),后者再传给 Obiect; : Obiect),这只有在 C++11 里通过perfect forwarding(完美转发) 才能完美解决。
2.4 weak_ptr
2.4.1 weak_ptr理解
weak_ptr 是为了配合 shared ptr 而引入的一种智能指针,它指向一个由 shared ptr 管理的对象而不影响所指对象的生命周期,也就是将一个 weak_ptr 绑定到一个 shared_ptr 不会改变 shared_ ptr 的引用计数。
即weak_ptr是为了协助shared_ptr而出现的。它不能访问对象,只能观测shared_ptr的引用计数,防止出现死锁。
不论是否有 weak ptr 指向,一旦最后一个指向对象的 shared_ptr 被销毁,对象就会被释放。从这个角度看weak_ptr 更像是 shared_ptr 的一个助手而不是智能指针。
2.4.2 为什么会有weak_ptr
在出现了循环引用(或环形引用)的情况下,shared_ptr。
class Child;class Parent
{
public:shared_ptr<Child> child;~Parent(){cout<< "Destory Parent"<< endl;}void Priant() const{cout<< "Parent-----------"<< endl;}
};class Child
{
public:shared_ptr<Parent> parent;~Child(){cout<< "Destory Child"<< endl;}
};int main()
{shared_ptr<Parent> parent = make_shared<Parent>();shared_ptr<Child> child = make_shared<Child>();parent->child = child;child->parent = parent;child->parent->Priant();return 0;
}
上面代码的运行结果,只打印出”Parent-----------”,而并没有打印出"Destory Parent”或”Destory Child",说明 Parent 和 Child 的析构函数并没有调用到。这是因为Parent 和 Child 对象内部,具有各自指向对方的 shared_ptr,加上 parent和 child 这两个shared_ptr,说明每个对象的引用计数都是 2。当程序退出时,即使 parent 和 child 被销毁,也仅仅是导致引用计数变为了1,因此并未销毁 Parent 和 Child 对象。
2.3.3 weak_ptr使用
创建weak_ptr实例
创建一个weak_ptr时,需要用一个 shared_ptr 实例来初始化weak_ptr,由于是弱共享,weak_ptr的创建并不会影响 shared_ptr 的引用计数值
int main()
{shared_ptr<int> sp = make_shared<int>(5);cout<< sp.use_count()<< endl; //1weak_ptr<int> wp(sp);cout<< sp.use_count()<< endl; //1cout<< wp.use_count()<< endl; //1return 0;
}
判断weak_ptr指向对象是否存在
weak_ptr 并不改变其所共享的 shared_ptr 实例的用计数,那就可能存在 weak ptr 指向的对象被释放掉这种情况。这时,我们就不能使用 weak ptr 直接访问对象。那么我们如何判断 weak ptr 指向对象是否存在呢?C++中提供了 lock 函数来实现该功能。
如果对象存在,lock(函数返回一个指向共享对象的 shared ptr,否则返回一个空 shared ptr。
shared_ptr<int> sp = make_shared<int>(5);//sp = nullptr;weak_ptr<int> wp(sp);if(shared_ptr<int> pa = wp.lock()){cout<< *pa <<endl;
}
else{cout<< wp.expired()<< endl; weak_ptr 还提供了 expired0函数来判断所指对象是否已经被销毁cout<< "*wp is nullptr"<< endl;
}
weak_ptr使用
weak_ptr 并没有重载 operator->和 operator 操作符,因此不可直接通过 weak_ptr 使用对象,典型的用法是调用其lock 函数来获得 shared_ptr 示例,进而访问原始对象。
最后,我们来看看如何使用 weak_ptr 来改造最前面的代码,打破循环引用问题。
class Child;class Parent
{
public:weak_ptr<Child> child;~Parent(){cout<< "Destory Parent"<< endl;}void Priant() {cout<< "Parent-----------"<< endl;}
};class Child
{
public:weak_ptr<Parent> parent;~Child(){cout<< "Destory Child"<< endl;}
};int main()
{shared_ptr<Parent> parent1 = make_shared<Parent>();shared_ptr<Child> child1 = make_shared<Child>();weak_ptr<Parent> parent(parent1);weak_ptr<Child> child(child1);parent1->child = child1;child1->parent = parent1;//child1->parent.lock()->Priant();if(!child.expired()){child1->parent.lock()->Priant();}return 0;
}
2.5 几种智能指针的比较
unique_ptr和 shared ptr 类型指针有很大的不同: shared ptr 允许多个指针指同一对象,而 nique_ptr 在某时刻只能有一个指针指向该对象(两个 unique ptr 不能指向同一个对象)。
-
使用场景:
如果程序要使用多个指向同一个对象的指针,应该选择 shared_ptr;
如果程序要使用一个指向一个对象的指针,则可以使用 unique_ptr;
如果使用 new [] 分配内存,应该选择 unique_ptr;
如果函数使用 new 分配内存,并返回指向该内存的指针,将其返回类型声明为 unique_ptr 是不错的选择。 -
智能指针实现原理:建立所有权(ownership)概念。
auto_ptr 和 unique_ptr 的策略:对于特定的对象,只能有一个智能指针可拥有它,这样只有拥有对象的智能指针的析构函数会删除该对象。然后,让赋值操作转让所有权。但 unique_ptr 的策略更严格,unique_ptr 能够在编译期识别错误。
shared_ptr 采用的策略。:跟踪引用特定对象的智能指针计数,这称为引用计数(reference counting)。例如,赋值时,计数将加 1,而指针过期时,计数将减 1. 仅当最后一个指针过期时,才调用 delete。 -
线程安全:
shared_ptr:引用计数在手段上使用了 atomic 原子操作,只要 shared_ptr 在拷贝或赋值时增加引用,析构时减少引用就可以了。首先原子是线程安全的,所有 shared_ptr 智能指针在多线程下引用计数也是安全的,也就是说 shared_ptr 智能指针在多线程下传递使用时引用计数是不会有线程安全问题的。 但是指向对象的指针不是线程安全的,使用 shared_ptr 智能指针访问资源不是线程安全的,需要手动加锁解锁。智能指针的拷贝也不是线程安全的。
相关文章:
RAII与智能指针
RAII与智能指针 1.RAII1.1RAII理解1.2RAII的原理1.2.1简单的例子说明局部对象的自动销毁的特性 1.2.2 RAII 过程 2.智能指针2.1 auto_ptr2.1.1auto_ptr的使用构造函数与析构函数拷贝构造函数与赋值提领操作auto ptr其它函数 2.1.2autoptr使用的注意事项 2.2 unique_ptr2.2.1uni…...
易云维智慧工业云平台助力广西国企培育数字产业化平台,打造数字化产业生态
2022年6月6日,广西壮族自治区人民政府国有资产监督管理委员会出台了《国有企业数字化转型工作实施意见》,明确了总体要求、主要目标、重点推进国有企业数字化转型的任务和保障措施。 《意见》预计,到2025年底,广西基本实现自治区级…...
【密码学】第三章、分组密码
DES、IDEA、AES、SM4 1、分组密码定义(按照五个组成部分答) 密钥空间:属于对称加密算法kekd明密文空间:将明文划分为m比特的组,每一块依次进行加密加解密算法:由key决定一个明文到密文的可逆映射 2、发展…...
宁夏企业过等保选哪家测评机构好?选哪家堡垒机好?
最近不少宁夏小伙伴在问,宁夏企业过等保选哪家测评机构好?选哪家堡垒机好?今天我们小编就给大家来简单说说哈! 宁夏企业过等保选哪家测评机构好? 目前宁夏正规具有资质的等保测评机构只有3家,分别为中电信…...
Vue绑定style和class 对象写法
适用于:要绑定多个样式,个数确定,名字也确定,但不确定用不用。 绑定 class 样式【对象写法】: .box{width: 100px;height: 100px; } .aqua{background-color: aqua; } .border{border: 20px solid red; } .radius{bor…...
使用vue-sign插件
Vue-sign是一个基于Vue.js和Canvas的签名组件,可以轻松地在Vue应用程序中实现签名板或手写签名功能。使用它可以使用户在前端网页上直接进行手写签名,实现更好的用户交互和业务需求。 使用Vue-sign可以提供以下好处: 通过Vue.js框架进行开发…...
python究竟可以用来做些什么
这里就不撰述python的一些像什么“高级语言”之类的比较常规的介绍了,还是老样子,说说一些比较常用的东西吧。 python是什么 python,一款可编程的开源软件,很多第三方库、框架也是开源的,比如强大的anaconda、sklear…...
Segment Anything(论文解析)
Segment Anything 摘要1.介绍2 SAM任务SAM模型 摘要 我们介绍了“Segment Anything” (SA) 项目:这是一个新的任务、模型和数据集对于图像分割。使用我们高效的模型进行数据收集,我们构建了迄今为止最大的分割数据集(远远超过其他数据集&…...
@ConditionalOnProperty 用法
文章目录 前言一、使用场景二、使用步骤1.错误示例2.ConditionalOnProperty的解决方案 总结 前言 ConditionalOnProperty 是Spring Boot中的条件注解,它的核心功能是通过属性名以及属性值来实现的,常被用于判断某个属性是否存在,然后决定某个…...
如何选择超声波清洗机、超声波清洗机排行榜
眼镜的日常清洗生活中很多人都会把它给忘记!长时间下来眼镜支架就会变成黄色的,非常的难洗掉,从而又浪费了一个眼镜。一副好的眼镜也不便宜的,把换眼镜的钱省下来入一款超声波清洗机,可以大大的减少金钱的支持…...
大家这么喜欢这件羽绒服的吗?眼光太好啦
简单干净散发着朝气,温暖的气息由内而外 90白鸭绒,高密度充绒量和蓬松度 三防工艺,立领连帽设计 下摆抽绳,帽子上的魔术贴设计 无一不将保暖落实在实处...
pytorch 入门(二)
本文为🔗小白入门Pytorch内部限免文章 🍨 本文为🔗小白入门Pytorch中的学习记录博客🍦 参考文章:【小白入门Pytorch】教案二🍖 原作者:K同学啊 目录 一、神经网络的组成部分1. 神经元2. 神经网络…...
2023年国赛-大数据应用开发(师生同赛)_赛项规程样题解析
2023年国赛-大数据应用开发(师生同赛)_赛项规程样题解析-任务B:离线数据处理_子任务一:数据抽取2023年国赛-大数据应用开发(师生同赛)_赛项规程样题解析-任务B:离线数据处理_子任务二:数据清洗2023年国赛-大数据应用开发(师生同赛…...
MNE系列教程1——MNE的安装与基本绘图
一、MNE包简介 MNE-Python是一个强大的Python库,专门用于脑电图(EEG)和磁共振成像(MRI)数据的分析和可视化。它提供了广泛的工具,使研究人员能够高效地处理神经科学数据。 MNE-Python支持许多数据格式,包括标准的EEG和MEG文件格式,以及不同类型的MRI数据。它可以用于…...
黑马JVM总结(三十六)
(1)CAS-概述 cas是配合volatile使用的技术 ,对共享变量的安全性要使用synachonized加锁,但是CAS不加锁,它是使用where(true)的死循环,里面compareAndSwap尝试把结果赋值给共享变量&…...
【React】01-React的入门
文章目录 1.1 React简介1.1.1 官网1.1.2 介绍描述1.1.3 React的特点1.1.3 React高效的原因 1.2.React的基本使用1.2.2.相关js库1.2.3.创建虚拟DOM的两种方式1.2.4.虚拟DOM与真实DOM 1.3.React JSX1.3.1.效果jsx语法规则:1.3.2.JSX1.3.3.渲染虚拟DOM(元素)1.3.4.JSX练…...
【C语言进阶】自定义类型:结构体,枚举,联合
自定义类型:结构体,枚举,联合 1.结构体1.1结构体类的基础知识1.2结构的声明1.3特殊的声明1.4结构的自引用1.5结构体变量的定义和初始化1.6结构体内存对齐1.7修改默认对齐1.8结构体传参 2.段位2.1什么是段位2.2段位的内存分配2.3位段的跨平台问…...
Sklearn 聚类算法的性能评估
聚类算法的性能评估是什么? 聚类是无监督学习的一种常用技术,用于将相似的数据点分组在一起。然而在实施聚类算法后,一个关键的问题便是如何评估其性能或质量。由于聚类是无监督的,因此评估其性能相对更为复杂。本文将探讨多种用于评估聚类性能的指标,包括肘部法则、轮廓…...
9月最新外贸进出口数据出来了,外贸整体向好
10月13日,海关总署发布数据显示,今年前三季度中国货物贸易出口2.52万亿美元,下降5.7%。 9月当月,中国出口2991.3亿美元,同比下降6.2%。贸易顺差777.1亿美元。 这个数据还是在改善的。特别是,我们看到全球…...
SSL证书有效期越来越短是什么原因?
随着互联网的普及和数据安全意识的提高,SSL证书的使用变得日益普遍。SSL证书是一种用于加密数据传输并验证网站身份的安全协议。它们通过加密在用户浏览器和网站服务器之间传输的数据,从而确保数据的隐私和完整性。此外,SSL证书还通过数字签名…...
【前段基础入门之】=>CSS3新特性 3D 变换
导语 在上一章节中,我们分享了2D 变换的效果,也分享了一些案例,同时,既然有2D 变换,那么也就肯定有 3D 变换 那么本章节,就为大家带来有关3D 变换的分享. 文章目录 开启3D空间设置景深透视点位置3D 位移3D …...
form表单的三种封装方法(Vue+ElementUI)
form表单的三种封装方法(VueElementUI) 1.首先是最普通,也是大家最先想到的方法,直接封装:2.实现表单动态渲染、可视化配置的方法,动态表单又可以分为两种方法:(注意:注意 v-model 的…...
云原生周刊:CNCF 宣布 Cilium 毕业 | 2023.10.16
开源项目推荐 Reloader Reloader 是一个 Kubernetes 控制器,用于监控 ConfigMap 和 Secrets 中的变化,并对 Pod 及其相关部署、StatefulSet、DaemonSet 和 DeploymentConfig 进行滚动升级! Spegel Spegel 在瑞典语中意为镜像,…...
岩土工程监测利器:多通道振弦数据记录仪应用隧道监测
岩土工程监测利器:多通道振弦数据记录仪应用隧道监测 岩土工程监测在现代工程建设中的作用越来越重要。为了确保工程质量和工程安全,需要对工程过程中的各种参数进行实时监测和记录。而多通道振弦数据记录仪则是一种重要的监测工具,特别适用…...
hive排序
目录 order by (全局排序asc ,desc) sort by(reduce 内排序) Distribute by(分区排序) Cluster By(当 distribute by 和 sorts by 字段相同时 ,可以使用 ) order by (全局排序asc ,desc) INSERT OVERWRITE LOCAL DIRECTORY /home/test2 …...
网络安全入门教程(非常详细)从零基础入门到精通
网络安全是一个庞大而不断发展的领域,它包含多个专业领域,如网络防御、网络攻击、数据加密等。介绍网络安全的基本概念、技术和工具,逐步深入,帮助您成为一名合格的网络安全从业人员。 一、网络安全基础知识 1.计算机基础知识 …...
自动驾驶中的数据安全和隐私
自动驾驶技术的发展已经改变了我们的出行方式,但伴随着这项技术的普及,数据安全和隐私问题也变得愈发重要。本文将探讨自动驾驶中的数据收集、数据隐私和安全挑战,以及如何保护自动驾驶系统的数据。 自动驾驶中的数据收集 在自动驾驶技术中…...
回应:淘宝支持使用微信支付?
近日,就有网友共享称淘宝APP的支付界面出现“微信二维码支付”及其“去微信找个朋友帮我付”这个选项。 淘宝官方网对此回应称,“微信二维码支付作用仍在逐步开放中,目前只有针对一些客户对外开放,并且只有部分商品适用这一付款方…...
k8s的etcd启动报错
背景 电脑休眠状态意外断电导致虚拟机直接进入关机状态。 问题 kubectl命令报错 [rootmaster01 ~]#kubectl get node The connection to the server master01.kktb.org:6443 was refused - did you specify the right host or port?kubelet服务报错 Oct 15 08:39:37 mas…...
codeigniter 4.1.3 gadget chain
EXP code 找到一条很有意思的codeigniter框架的链。 <?php namespace CodeIgniter\HTTP {class CURLRequest {protected $config ["debug" > "./eee.php"];} }namespace CodeIgniter\Session\Handlers {class MemcachedHandler{public function …...
360网站建设/西安seo培训
成为Excel精英真不容易啊,正则表达式必须拿下! ()可以让括号内作为一个整体产生重复Sub t29()Dim regx As New RegExpDim srsr "A3A3QA3A37BDFE87A8"With regx.Global True.Pattern "((A3){2})" 相当于A3A3Debug.Print .Replace(…...
漳州哪里做网站/会计培训班要多少钱一般要学多久
首先预览下,本次发布的核心内容 :精细化异常信息输出,以便快速的定位问题[Feature] 🆕#666 com.feilong.core.lang.NumberUtil.getAddValue(Number…) 要允许null值所有数加起来.说明:支持跳过null 元素相加 (since 1.11.5)但是如果所有元素都是null ,将会抛出 Ille…...
南京政府门户网站建设问题/百度推广客服
DedeCMS的HTML更新 为了减轻网站负载,提高搜索引擎的友好度,DedeCMS大多数内容都需要生成HTML,一般的操作如下: (推荐学习:dedecms教程) 1、发布内容(发布时会直接生成文档的HTML) 2、更新内…...
进行优化/郑州网站seo
OpenJDK中同时会有好几个项目在进行中。这些项目所带来的修改可能会加入到以后版本的JDK中。本文带你看一下OpenJDK中正在进行的重要项目,提前了解以后版本的JDK中会增加的功能。AmberAmber项目关注的是Java语言的小改动。Amber项目的一些成果已经被添加到JDK中。这…...
wordpress jfinal/免费引流推广工具
本节大纲: 一:双层装饰器:一个函数可以被多层装饰器进行装饰,函数渲染(编译)从下到上,函数执行从上到下。如下程序: 1 #!/usr/bin/env python2 #-*-coding:utf-8-*-3 # author:liume…...
找人做网站需要注意什么问题/百度帐号登录入口
给出如下结构体,要求全部在堆区开辟空间。有多个老师,每个老师有多个学生。 typedef struct {char *name;//老师int stu_num;//学生数char **stu_arr;//学生名字 }TEACHER; 需要注意以下问题: 此问题使用双层循环即可解决。外层循环处理这…...