C++11新特性 智能指针

智能指针

  • nuique_ptr
    • 特点
    • 不允许拷贝构造和赋值
    • 运算符重载-> () *
    • unique_ptr 删除器仿写
      • 删除文件
      • 删除普通对象
  • shared_ptr
    • 特点
    • 示意图
    • 仿写shared_ptr
      • 删除器部分特化
      • 拷贝构造 移动构造 && 左值赋值 和移动赋值
      • 完整实现
  • weak_ptr
    • 特点
    • weak_ptr 实现
    • 解决循环引用
    • 弱指针一个应用

nuique_ptr

unique_ptr在要表达"专属所有权"的语义时使用,即unique_ptr指针永远′拥有"其指向的对象,所以unique_ptr是一个move-only类型,一个unique_ptr指针是无法被复制的,只能将"所有权"在两个unique_ptr指针之间转移,转移完成后原来的unique_ptr将被设为null;
智能指针是比原始指针更智能的类,解决悬空(dangling)指针或多次删除被指向对象,以及资源泄露问题,通常用来确保指针的寿命和其指向对象的寿命一致。智能指针虽然很智能,但容易被误用,智能也是有代价的。

特点

1 .基于排他所有权模式:两个指针不能指向同一个资源。这一块资源只能被一个指针指向
2.由于独占对象的拥有权,所以不提供拷贝构造函数和左值赋值函数重载。浅拷贝构造和浅赋值都是两个对象同时指向一个资源,
深拷贝,深赋值,同样的资源复制了一份,被两个unique对象指向,不行。
3.提供移动构造和移动赋值函数。
4 .为了实现单个对象和一组对象的管理,添加了删除器类型。
5.在容器保存指针是安全。
6.unique_ptr具有->和*运算符重载符,因此它可以像普通指针一样使用。


class PtrInt {
private:int* pval;
public:PtrInt(int x=0):pval(new int(x)){cout << "Create PtrInt" << this << endl;}~PtrInt() {delete pval;pval = nullptr;cout << "Destroy PtrInt" << this << endl;}PtrInt(const PtrInt& it) :pval(new int(10)) {if (it.pval != nullptr) {*pval = *it.pval;}cout  << this<< "Copy Create PtrInt" <<&it<< endl;}PtrInt& operator=(const PtrInt& it) {if (this != &it) {delete pval;if (it.pval != nullptr) {pval = new int(*it.pval);}}cout << this << "operator=()" << &it << endl;return *this;}PtrInt(PtrInt&& it) :pval(it.pval) {it.pval = nullptr;cout<<this << "Move Create PtrInt" << &it << endl;}PtrInt& operator=(PtrInt&& it) {if (this != &it) {delete[]pval;pval = it.pval;it.pval = nullptr;}cout << this << "Move operatro=()" << &it << endl;return *this;}void SetValue(int x) { *pval = x; }int GetValue()const { return *pval; }void Print()const {if (pval != nullptr) {cout << *pval << endl;}}};template<class _Ty>
class my_unique_ptr {
public:using pointer = _Ty*;using element_type = _Ty;
private:pointer mPtr;
public:my_unique_ptr(_Ty* p = nullptr) :mPtr(p){}//唯一性指针没有拷贝构造和左值赋值my_unique_ptr(const my_unique_ptr&) = delete;my_unique_ptr& operator=(const my_unique_ptr&) = delete;~my_unique_ptr() {if (mPtr != nullptr) {delete mPtr;}mPtr = nullptr;}
};int main() {unique_ptr<PtrInt>pa(new PtrInt(10));pa->Print();return 0;
}

在这里插入图片描述
my_unique_ptrpa(new PtrInt(100));
在这里插入图片描述

不允许拷贝构造和赋值

唯一性智能指针不允许拷贝构造:浅拷贝pa pb的mPtr会指向同一个空间,析构时会把同一个地址释放两次,深拷贝就有了两份空间,应该只有一份

my_unique_ptr(const my_unique_ptr&) = delete;

也不允许赋值

my_unique_ptr& operator=(const my_unique_ptr&) = delete;

可以实现移动拷贝构造和移动赋值
移动之后,资源就不是以前的了,强行访问会崩溃

my_unique_ptr(my_unique_ptr&& x) :mPtr(x.mPtr) {x.mPtr = nullptr;
}//pa=move(pb);  //unique_
my_unique_ptr operator=(my_unique_ptr&& other) {if (this == &other)return *this;//my_unique_ptr(move(other)).swap(*this); 与下面三行等价delete mPtr;mPtr = other.mPtr;other.mPtr = nullptr;return *this;
}

运算符重载-> () *

在这里插入图片描述
pa是一个对象,访问对象的成员函数用.
为啥可以pa->
-> 都是用在结构体指针,->就是 先解引用,再 . 访问对象成员函数
这里重载了->
pa->Print();
pa.operator->()->Print();
等价

	pointer get()const { return mPtr; }_Ty& operator*()const { return *get(); }_Ty* operator->()const { return get(); }

在这里插入图片描述

int main() {unique_ptr<Int>pa(new Int(100));pa->Print();pa.operator->()->Print();return 0;
}
class Int {int value;
public:Int(int x = 0) :value(x) {cout << "Create Int " << endl;}~Int() { cout << "Destroy Int " << endl; }void SetValue(int x) { value = x; }int GetValue()const {return value;}void Print()const { cout << value << endl; }
};template<class _Ty>
class my_unique_ptr {
public:using pointer = _Ty*;using element_type = _Ty;
private:pointer mPtr;
public:my_unique_ptr(_Ty* p = nullptr) :mPtr(p){}my_unique_ptr(const my_unique_ptr&) = delete;my_unique_ptr& operator=(const my_unique_ptr&) = delete;my_unique_ptr(my_unique_ptr&& x) :mPtr(x.mPtr) {x.mPtr = nullptr;}~my_unique_ptr() {if (mPtr != nullptr) {delete mPtr;}mPtr = nullptr;}pointer get()const { return mPtr; }_Ty& operator*()const { return *get(); }_Ty* operator->()const { return get(); }};int main() {unique_ptr<Int>pa(new Int(100));pa->Print();pa.operator->()->Print();return 0;
}

在这里插入图片描述
重载返回的mPtr ,*pa直接就是int对象,所以可以直接修改

int main() {unique_ptr<int>pa(new int(10));*pa = 100;pa.operator*() = 100;my_unique_ptr<Int>pb(new Int(10));//*pb返回的是一个Int对象 对象用. 访问成员函数Print()(*pb).Print();//上面已经解释过了,重载->pb->Print();return 0;
}

在这里插入图片描述

template<class _Ty>
class my_unique_ptr {
public:using pointer = _Ty*;using element_type = _Ty;
private:pointer mPtr;
public:my_unique_ptr(_Ty* p = nullptr) :mPtr(p){}my_unique_ptr(const my_unique_ptr&) = delete;my_unique_ptr& operator=(const my_unique_ptr&) = delete;my_unique_ptr(my_unique_ptr&& x) :mPtr(x.mPtr) {x.mPtr = nullptr;}//pa=move(pb);  //unique_my_unique_ptr operator=(my_unique_ptr&& other) {if (this == &other)return *this;//my_unique_ptr(move(other)).swap(*this); 与下面三行等价delete mPtr;mPtr = other.mPtr;other.mPtr = nullptr;return *this;}~my_unique_ptr() {if (mPtr != nullptr) {delete mPtr;}mPtr = nullptr;}pointer get()const { return mPtr; }_Ty& operator*()const { return *get(); }_Ty* operator->()const { return get(); }pointer release() {pointer old = mPtr;mPtr = nullptr;return old;}void reset(pointer ptr = nullptr) {if (mPtr != nullptr) {delete mPtr;}mPtr = ptr; }void swap(my_unique_ptr& ptr) {swap(this->mPtr, it.mPtr);}
};

unique_ptr 删除器仿写

对申请的系统资源,重写一个删除器类型,专门针对系统资源释放,如果是new
则需要delete 如果是文件fopen,则需要用fclose

删除文件

template<class _Ty,class _Dx=my_default_deleter<_Ty>>
class my_unique_ptr {
public:using pointer = _Ty*;using element_type = _Ty;using deleter_type = _Dx;
private:pointer mPtr;deleter_type deleter;
public:deleter_type get_deleter()const { return deleter; }my_unique_ptr(_Ty* p = nullptr) :mPtr(p){}my_unique_ptr(const my_unique_ptr&) = delete;my_unique_ptr& operator=(const my_unique_ptr&) = delete;my_unique_ptr(my_unique_ptr&& x) :mPtr(x.mPtr) {x.mPtr = nullptr;}//pa=move(pb);  //unique_my_unique_ptr operator=(my_unique_ptr&& other) {if (this == &other)return *this;//my_unique_ptr(move(other)).swap(*this); 与下面三行等价delete mPtr;mPtr = other.mPtr;other.mPtr = nullptr;return *this;}~my_unique_ptr() {if (mPtr != nullptr) {get_deleter() (mPtr);}mPtr = nullptr;}pointer get()const { return mPtr; }_Ty& operator*()const { return *get(); }_Ty* operator->()const { return get(); }pointer release() {pointer old = mPtr;mPtr = nullptr;return old;}void reset(pointer ptr = nullptr) {if (mPtr != nullptr) {get_deleter()(mPtr);}mPtr = ptr; }void swap(my_unique_ptr& ptr) {swap(this->mPtr,ptr.mPtr);}
};
struct FileDeleter {void operator()(FILE*ptr)const  {fclose(ptr);}
};
int main() {
my_unique_ptr<FILE>pa(fopen("xst.txt","w"));fprintf(pa.get(), "yhping\n");return 0;
}

删除普通对象

一组对象和单个对象的delete

template<class _Ty>
struct my_default_deleter {void operator()(_Ty* ptr)const {delete ptr;}
};
template<class _Ty>
struct my_default_deleter<_Ty[]> {void operator()(_Ty* ptr)const {delete[] ptr;}
};template<class _Ty, class _Dx = my_default_deleter<_Ty>>
class my_unique_ptr{
public:using pointer = _Ty*;using element_type = _Ty;using deleter_type = _Dx;
private:pointer mPtr;deleter_type deleter;
public:deleter_type get_deleter()const { return deleter; }my_unique_ptr(_Ty* p = nullptr) :mPtr(p){}my_unique_ptr(const my_unique_ptr&) = delete;my_unique_ptr& operator=(const my_unique_ptr&) = delete;my_unique_ptr(my_unique_ptr&& x) :mPtr(x.mPtr) {x.mPtr = nullptr;}//pa=move(pb);  //unique_my_unique_ptr operator=(my_unique_ptr&& other) {if (this == &other)return *this;//my_unique_ptr(move(other)).swap(*this); 与下面三行等价delete mPtr;mPtr = other.mPtr;other.mPtr = nullptr;return *this;}~my_unique_ptr() {if (mPtr != nullptr) {get_deleter() (mPtr);}mPtr = nullptr;}pointer get()const { return mPtr; }_Ty& operator*()const { return *get(); }_Ty* operator->()const { return get(); }pointer release() {pointer old = mPtr;mPtr = nullptr;return old;}void reset(pointer ptr = nullptr) {if (mPtr != nullptr) {get_deleter()(mPtr);}mPtr = ptr; }void swap(my_unique_ptr& ptr) {swap(this->mPtr,ptr.mPtr);}
};class Int {int value;
public:Int(int x = 0) :value(x) {cout << "Create Int " << endl;}~Int() { cout << "Destroy Int " << endl; }void SetValue(int x) { value = x; }int GetValue()const {return value;}void Print()const { cout << value << endl; }
};int main() {my_unique_ptr<Int[]>pa(new Int[10]{1,2,3,4,5,6,7,8,9,10});for (int i = 0; i < 10; i++) {pa[i].Print();}return 0;
}

在这里插入图片描述

int main() {
std::unique_ptr<Int> pa(new Int(10));
std::unique_ptr<Int> pb = std::make_unique<Int>(20);
std::unique_ptr<Int[]> pca(new Int[10]{ 1,2,3,4,5,6,7,8,9,10 }); 
std::unique_ptr<Int[]> pcb = std::make_unique<Int[]>(10);//不能初始化
return 0;
}

shared_ptr

shared_ptr实现了共享所有权(shared ownership)方式来管理资源对象,这意味没有一个特定的std::shared_ptr拥有资源对象。相反,这些指向同一个资源对象的stdushared_ptr相互协作来确保该资源对象在不需要的时候被析构。

特点

1 .基于共享所有权模式:多个指针能够同时指向同一个资源。
2.基于共享所有权,使用引用计数控制块管理资源对象的生命期。
3.提供拷贝构造函数和赋值重载函数;提供移动构造和移动赋值函数。
4 .为了实现单个对象和一组对象的管理,添加了删除器类型。
5.在容器保存shared_ptr对象是安全。
6 .shared_ptr重载了operator->和operator*运算符,因此它可以像普通指针一样使用。
两个共享指针指向同一个资源,该资源的引用计数加一

示意图

和唯一性指针的区别是加了一个引用计数块,一个资源一个引用计数块
在这里插入图片描述

int main() {shared_ptr<int>pa(new int(10));cout << pa.use_count() << endl;shared_ptr<int>pb(pa);cout << pb.use_count();return 0;
}

在这里插入图片描述

atomic<int>_Uses;

原子操作,使其过程不能被打断。

	~my_shared_ptr() {if (mRep != nullptr && mRep->Decref() == 0) {mDeleter(mPtr);delete mRep;}

资源需要用删除器删除,因为资源可能是API构建,释放也要API释放,而引用计数是new 所以释放 就是 delete
在这里插入图片描述

仿写shared_ptr

删除器部分特化

template<class _Ty>
struct My_Deleter
{
public:void operator()(_Ty* p) const{delete p;}
};
template<class _Ty>
struct My_Deleter<_Ty[]>
{
public:void operator()(_Ty* p) const{delete []p;}
};

拷贝构造 移动构造 && 左值赋值 和移动赋值

拷贝构造实现的时候,两个shared_ptr指向的是同一个资源,同一个引用技术块,引用的uses+1

My_shared_ptr(const My_shared_ptr& x):mPtr(x.mPtr), mRep(x.mRep){if (mRep != nullptr){mRep->Incref();}}My_shared_ptr(My_shared_ptr&& other):mPtr(other.mPtr),mRep(other.mRep){mPtr = nullptr;mRep = nullptr;}

分析一下这个程序:左值赋值
My_shared_ptr(x) 类型名加括号,构建不具名对象,调用拷贝构造,引用计数加1,是个将亡值,然后析构将亡值,引用计数减1,发现uses为0 ,就删除了 pa指向的资源和引用计数块,
如果pa指向的资源uses不是1,那么就不会删除该资源和引用计数块
在这里插入图片描述
移动赋值
如果原来的资源的引用计数为1,移动后需要释放,不为1 就减1
移动赋值后,指针pb就跟原来的资源么有多了关系,pb->printf 会崩,而左值赋值,有关系
std::move(other) 转成右值,

	My_shared_ptr& operator=(const My_shared_ptr& x){if (this == &x) return *this;My_shared_ptr(x).swap(*this);return *this;}My_shared_ptr& operator=(My_shared_ptr&& other){if (this == &other) return *this;My_shared_ptr(std::move(other)).swap(*this);return *this;}

完整实现

#include <iostream>
#include <string>
#include <vector>
#include <map>
#include <algorithm>
#include <memory>
#include<atomic>
using namespace std;
class Int
{
private:int value;
public:Int(int x = 0) :value(x) { cout << "Create Int: " << endl; }Int(int x, int y) :value(x + y) { cout << "Create Int(int,int)" << endl; }~Int() { cout << "Destroy Int " <<value<<endl; }Int(const Int& it) :value(it.value){cout << "copy Create Int: " << this << endl;}Int& operator=(const Int& it){if (this != &it){value = it.value;}cout << "operator=()" << endl;return *this;}void PrintInt() const{cout << value << endl;}int& Value() { return value; }const int& Value() const { return value; }operator int() const { return value; }
};template<class _Ty>
struct My_Deleter
{
public:void operator()(_Ty* p) const{delete p;}
};
template<class _Ty>
struct My_Deleter<_Ty[]>
{
public:void operator()(_Ty* p) const{delete []p;}
};template<class _Ty>
class My_RefCount
{
public:using element_type = _Ty;using pointer = _Ty*;
private:_Ty* _Ptr;std::atomic<int> _Uses; // shared;std::atomic<int> _Weaks; // weak_ptr;
public:My_RefCount(_Ty* ptr = nullptr) :_Ptr(ptr), _Uses(0), _Weaks(0){if (_Ptr != nullptr){_Uses = 1;_Weaks = 1;}}~My_RefCount() = default;void Incref() { ++_Uses; }void Incwref() { ++_Weaks; }int Decref(){if (--_Uses == 0){Decwref();}return _Uses;}int Decwref(){return --_Weaks;}int _use_count() const{return _Uses.load();}
};template<class _Ty,class _Dx = My_Deleter<_Ty>>
class My_shared_ptr
{
public:using MyDeleter = _Dx;
private:_Ty*               mPtr;My_RefCount<_Ty> * mRep;MyDeleter          mDeleter;
public:My_shared_ptr(_Ty* ptr = nullptr) :mPtr(ptr), mRep(nullptr){if (mPtr != nullptr){mRep = new My_RefCount<_Ty>(mPtr);}}My_shared_ptr(const My_shared_ptr& x):mPtr(x.mPtr), mRep(x.mRep){if (mRep != nullptr){mRep->Incref();}}My_shared_ptr(My_shared_ptr&& other):mPtr(other.mPtr),mRep(other.mRep){mPtr = nullptr;mRep = nullptr;}My_shared_ptr& operator=(const My_shared_ptr& x){if (this == &x) return *this;My_shared_ptr(x).swap(*this);return *this;}My_shared_ptr& operator=(My_shared_ptr&& other){if (this == &other) return *this;My_shared_ptr(std::move(other)).swap(*this);return *this;}~My_shared_ptr(){if (nullptr != mRep && 0 == mRep->Decref()){mDeleter(mPtr);delete mRep;}mPtr = nullptr;mRep = nullptr;}int use_count() const{return mRep != nullptr ? mRep->_use_count() : 0;}_Ty * get() const { return mPtr; }_Ty& operator*() const { return *get(); }_Ty* operator->() const {return get();}operator bool() const { return mPtr != nullptr; }void swap(My_shared_ptr& other){std::swap(this->mPtr, other.mPtr);std::swap(this->mRep, other.mRep);}void reset(){if (mRep != nullptr && 0 == mRep->Decref()){mDeleter(mPtr);delete mRep;}mRep = nullptr;mPtr = nullptr;}void reset(_Ty* p){if (nullptr == p){reset();return;}if (mRep != nullptr && 0 == mRep->Decref()){mDeleter(mPtr);delete mRep;mRep = nullptr;}mPtr = p;if (mPtr != nullptr){mRep = new My_RefCount(mPtr);}}
};

weak_ptr

特点

弱引用指针weak_ptr是用来监视shared_ptr的生命周期,是shared_ptr的一个助手。weak_ptr没有重载操作符*和->,因为它不与shared_ptr共享指针,不能操作资源,主要是通过shared_ptr获得资源的监测权,它的构造不会增加引用计数,它的析构也不会减少引用计数,纯粹只是作为一个旁观者来监视shared_ptr中管理的资源是否存在。weak_ptr还可以用来返回this 指针和解决循环引用的问题。

当我们创建一个weak_ptr时,需要用一个shared_ptr实例来初始化weak_ptr,由于是弱共享,weak_ptr的创建并不会影响shared_ptr的引用计数值。可以通过use_count()方法来获得当前观测资源的引用计数。

weak_ptr 实现

template<class _Ty>
class My_weak_ptr
{
private:My_RefCount<_Ty>* mRep;
public:My_weak_ptr() :mRep(nullptr) {}My_weak_ptr(const My_shared_ptr<_Ty>& other){mRep = other.mRep;if (mRep != nullptr){mRep->Incwref();}}~My_weak_ptr(){if (mRep != nullptr){mRep->Decwref();}}
};

解决循环引用

class Parent;
class Child
{
public://std::shared_ptr<Parent> parent;std::weak_ptr<Parent> parent;Child() { cout << "Create Child" << endl; }~Child() { cout << "~Child" << endl; }
};
class Parent
{
public://std::shared_ptr<Child> child;std::weak_ptr<Child> child;Parent() { cout << "Create Parent" << endl; }~Parent() { cout << "~Parent" << endl; }void Hi() { cout << "hello yhping" << endl; }
};
int main()
{std::shared_ptr<Parent> pa(new Parent());std::shared_ptr<Child>  cp(new Child());pa->child = cp;cp->parent = pa;return 0;
}

在这里插入图片描述
无法释放对象,因为use减为了1,调用析构函数时,无法删除 pa指向的资源和计数块
用弱指针就能解决该问题

弱引用指针只能对weaks加1,共享性指针只能对uses加一
在这里插入图片描述
只有当uses和weaks同时为0,才能删除该资源
uses为0可以析构资源,但是不能析构计数块,weaks为0 uses为0 才能析构计数块
过程就是:uses减1 weaks减1 释放child对象 ,该对象有mRep 所以 parent里面的weaks减1,此时析构child对象,此时析构pa,uses减1,为0,此时weaks为0,可以将该计数块析构,所以将parent对象释放,有弱引用指针,此时将最右边的计算计数块,weaks为0,
析构计数块,析构parent对象在这里插入图片描述

弱指针一个应用

应用就是
删除父节点,子节点也会被自动释放删除,析构子节点,父节点不会被析构

template<class KeyType>
class BSTree
{
public:struct BstNode{std::shared_ptr<BstNode> leftchild;std::weak_ptr<BstNode> parent;std::shared_ptr<BstNode> rightchild;KeyType key;public:BstNode(const KeyType& kx):key(kx){}~BstNode(){}};
private:std::shared_ptr<BstNode> root;static void InOrder(std::shared_ptr<BstNode> ptr){if (ptr){InOrder(ptr->leftchild);cout << ptr->key << " ";InOrder(ptr->rightchild);}}
public:BSTree() :root(nullptr) {}~BSTree(){}bool Insert(const KeyType& kx){if (!root)  // root.operator bool(){root = std::make_shared<BstNode>(kx);  // Root;return true;}std::shared_ptr<BstNode> pa(nullptr);std::shared_ptr<BstNode> p = root;while (p && p->key != kx){pa = p;p = kx > p->key ? p->rightchild : p->leftchild;}if (p && p->key == kx){cout << "key 重复 " << endl;return false;}p = std::make_shared<BstNode>(kx);p->parent = pa;if (p->key > pa->key){pa->rightchild = p;}else{pa->leftchild = p;}return true;}void InOrder() const{InOrder(root);cout << endl;}
};
int main()
{int ar[] = { 53,17,78,9,45,65,87,23,81,94,88 };BSTree<Int> mytree;Int tmp;for (auto& x : ar){tmp.Value() = x;mytree.Insert(tmp);}mytree.InOrder();return 0;
}
shared_ptr<Int>pa(new Int(10));
shared_ptr<Int>pb=make_shared<Int>(20);

区别是pa 用了两次new 第二次开辟计数块的空间,而pb开辟一次,

weaks就是只要是指针指向,就算一个,弱指针和共享指针都可以,而uses必须是shared指针,才算数
pb空间只有等weaks为0才能释放,mPtr指向的和mPep指向的绑在一起了

My_shared_ptr(_Ty* ptr = nullptr) :mPtr(ptr), mRep(nullptr)
{if (mPtr != nullptr){mRep = new My_RefCount<_Ty>(mPtr);}
}

创建共享性指针的时候,有两个参数 ,指定所需的删除器,唯一性指针不具有,弱指针不具备删除对象

shared_ptr<Int>pa(new Int(10),DeleteInt);

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

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

相关文章

事务

事务回顾MySQL事务Spring事务实现编程式事务实现&#xff1a;声明式事务 Transactional 注解作用范围及名称&#xff08;value/transactionManager&#xff09;隔离级别&#xff1a;isolation超时时间&#xff1a;timeout修改只读事务指定异常异常捕获情况 事务失效场景Transac…

九、ElasticSearch 运维 -集群维度

1. 查看集群健康 用于简单的判断集群的健康状态&#xff0c;集群内的分片的分配迁移情况。 GET _cluster/health-------------------------Respond----------------------------- {"cluster_name" : "test-jie","status" : "green",…

【Python】 Windows上通过git bash执行python卡住的解决方法

解决方法 编辑 C:\Program Files\Git\etc\profile.d\aliases.sh&#xff0c;将python2.7改成python 编辑完成后&#xff0c;重启git bash, 输入python即可 参考 https://blog.csdn.net/ofreelander/article/details/112058975

List, Set, Ordered-SetHash

前言 本文小结Redis中List&#xff0c;Set&#xff0c;ZSet和Hash四种数据类型的&#xff0c;基本特点&#xff0c;使用场景和实现方式。 一、List 1. 基本特点 a. 作为数组&#xff0c;基于下标索引操作, 但支持正向索引和反向索引; b. 作为链表, 支持高效插入&#xff1b…

Nginx安装、卸载教程(含Window、Linux版、Docker版)

目录 一、下载 二、Linux版安装 2.1 编译安装之前 2.2 编译安装 2.3 启动Nginx 2.4 关于防火墙 2.5 安装成系统服务 三、Linux版卸载&#xff08;彻底&#xff09; 3.1 检查一下Nginx服务是否在运行 3.2 停止Nginx服务 3.3 查找、删除Nginx相关文件 3.4 再使用yum清…

设计模式学习之模板方法模式

设计模式系列往期文章 设计模式学习之策略模式设计模式学习之策略模式在前端的应用设计模式学习之简单工厂模式设计模式学习之工厂方法模式设计模式学习之抽象工厂模式设计模式学习之策略模式和简单工厂模式的对比设计模式学习之观察者模式 模板方法模式是行为型设计模式的一…

基于深度学习的目标检测的介绍(Introduction to object detection with deep learning)

物体检测的应用已经深入到我们的日常生活中&#xff0c;包括安全、自动车辆系统等。对象检测模型输入视觉效果(图像或视频)&#xff0c;并在每个相应对象周围输出带有标记的版本。这说起来容易做起来难&#xff0c;因为目标检测模型需要考虑复杂的算法和数据集&#xff0c;这些…

回顾分类决策树相关知识并利用python实现

大家好&#xff0c;我是带我去滑雪&#xff01; 决策树&#xff08;Decision Tree&#xff09;是一种基本的分类与回归方法&#xff0c;呈树形结构&#xff0c;在分类问题中&#xff0c;表示预计特征对实例进行分类的过程。它可以认为是if-then规则的集合&#xff0c;也可以认为…

深入了解Promise机制并使用JS实现一个Promise(一)

前言 关于为什么会有Promise以及Promise的一些用法和基本机制可以参考之前的文章JS中的异步与Promise使用整体来说&#xff0c;Promise可以帮助我们很好的解决异步的问题&#xff0c;号称是异步的终极解决方案。在浏览器中Promise是使用C实现的&#xff0c;今天就使用js来实现…

win10系统下安装qt5.12.0软件

一、软件下载 1、Qt开源社区下载 下载地址&#xff1a;https://download.qt.io/archive/qt/5.12/5.12.10/qt-opensource-windows-x86-5.12.10.exe 社区地址&#xff1a; Index of /archive/qt/5.12/5.12.10 2、百度网盘下载 链接&#xff1a;https://pan.baidu.com/s/1Sqi…

四.流程控制语句

1、条件语句 Go 编程语言中 if 条件语句的语法如下&#xff1a; 1、基本形式 if 布尔表达式 { /* 在布尔表达式为 true 时执行 */ } If 在布尔表达式为 true 时&#xff0c;其后紧跟的语句块执行&#xff0c;如果false 则不执行。 package main import "fmt" fu…

Elasticsearch和Kibana的安装

Elasticsearch和Lucene的关系 Elasticsearch是一个高度可扩展的、开源的、基于 Lucene 的全文搜索和分析引擎。它允许您快速&#xff0c;近实时地存储&#xff0c;搜索和分析大量数据&#xff0c;并支持多租户。 Elasticsearch也使用Java开发并使用 Lucene 作为其核心来实现所…