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

淘宝官网首页电脑版手机登录/南宁seo费用服务

淘宝官网首页电脑版手机登录,南宁seo费用服务,wordpress 产品图片,wordpress的字体🌈欢迎来到C专栏~~智能指针 (꒪ꇴ꒪(꒪ꇴ꒪ )🐣,我是Scort目前状态:大三非科班啃C中🌍博客主页:张小姐的猫~江湖背景快上车🚘,握好方向盘跟我有一起打天下嘞!送给自己的一句鸡汤&…

🌈欢迎来到C++专栏~~智能指针


  • (꒪ꇴ꒪(꒪ꇴ꒪ )🐣,我是Scort
  • 目前状态:大三非科班啃C++中
  • 🌍博客主页:张小姐的猫~江湖背景
  • 快上车🚘,握好方向盘跟我有一起打天下嘞!
  • 送给自己的一句鸡汤🤔:
  • 🔥真正的大师永远怀着一颗学徒的心
  • 作者水平很有限,如果发现错误,可在评论区指正,感谢🙏
  • 🎉🎉欢迎持续关注!
    在这里插入图片描述

请添加图片描述

文章目录

  • 🌈欢迎来到C++专栏~~智能指针
    • 一. 为什么需要智能指针?
      • 😎智能指针的原理
    • 二. C++中的智能指针登场
      • 🥑auto_ptr
      • 🥑unique_ptr
      • 🥑shared_ptr
        • 🎨基本设计
        • 🎨模拟实现
        • 🎨线程安全问题
        • 🎨定制删除器
      • 🥑weak_ptr
        • 💦shared_ptr的循环引用问题
        • 💦weak_ptr解决循环引用
    • C++11和boost中智能指针的关系
    • 常见面试题
  • 📢写在最后

请添加图片描述

一. 为什么需要智能指针?

下面我们先分析一下下面这段程序有没有什么内存方面的问题?提示一下:注意分析MergeSort函数中的问题

int div()
{int a, b;cin >> a >> b;if (b == 0)throw invalid_argument("除0错误");return a / b;
}void Func()
{// 1、如果p1这里new 抛异常会如何?// 2、如果p2这里new 抛异常会如何?// 3、如果div调用这里又会抛异常会如何?int* p1 = new int;//p1抛异常,直接跳catch,没毛病int* p2 = new int;//p2跑异常,程序从这一步跳catch,p1无法释放,资源泄露cout << div() << endl; // div抛异常,调到catch,p1, p2无法释放,资源泄露delete p1;delete p2;
}int main()
{try{Func();}catch (exception& e){cout << e.what() << endl;}return 0;
}

问题分析:上面的问题分析出来我们发现有什么问题?

  • 会有严重的内存泄漏的问题:如果用户输入的除数为0,那么div函数中就会抛出异常,这时程序的执行流会直接跳转到主函数中的catch块中执行,最终导致func函数中申请的内存资源没有得到释放

对此我们需要重新捕获异常,捕获后先将之前申请的内存资源释放,然后再将异常重新抛出

还有一种方法就是:智能指针

//利用RAII思想设计delete资源的类
template<class T>
class Smartptr
{
public:Smartptr(T* ptr):_ptr(ptr){}~Smartptr(){delete _ptr;}T& operator*()  //解引用{return *ptr;}T* operator->() //自定义类型{return ptr;}private:T* _ptr;
};void Func()
{int* p1 = new int;//p1抛异常,直接跳catch,没毛病Smartptr<int> sp1(p1);//栈帧结束会调用析构函数Smartptr<int> sp2(new int);cout << div() << endl; // div抛异常,调到catch,p1, p2无法释放,资源泄露
}

代码中将申请到的内存空间交给了一个SmartPtr对象进行管理

  • 构造的时候,SmartPtr将传入的需要被管理的内存空间保存起来
  • SmartPtr对象析构时,SmartPtr的析构函数中会自动将管理的内存空间进行释放
  • 为了让SmartPtr对象能够像原生指针一样使用,还需要对*->运算符进行重载

如此一来,无论是正常返回,抛异常的返回,只要SmartPtr对象的生命周期结束就会调用其对应的析构函数,进而完成内存资源的释放。

😎智能指针的原理

实现智能指针时需要考虑以下三个方面的问题:

  1. 在对象构造时获取资源,接着控制对资源的访问使之在对象的生命周期内始终保持有效,最后在对象析构的时候释放资源
  2. *->运算符进行重载,使得该对象具有像指针一样的行为
  3. 智能指针的拷贝问题

运用了RAII的思想:RAII(Resource Acquisition Is Initialization)是一种利用对象生命周期来控制程序资源(如内存、文件句柄、网络连接、互斥量等等)的简单技术

我们实际上把管理一份资源的责任托管给了一个对象。这种做法有两大好处:

  • 不需要显式地释放资源
  • 采用这种方式,对象所需的资源在其生命期内始终保持有效

为什么要解决智能指针对象的拷贝问题呢?

对于当前实现的SmartPtr类,如果用一个SmartPtr对象来拷贝构造另一个SmartPtr对象,或是将一个SmartPtr对象赋值给另一个SmartPtr对象,都会导致程序崩溃

int main()
{SmartPtr<int> sp1(new int);SmartPtr<int> sp2(sp1); //拷贝构造SmartPtr<int> sp3(new int);SmartPtr<int> sp4(new int);sp3 = sp4; //拷贝赋值return 0;
}

编译器默认生成的拷贝构造对内置类型完成值拷贝,即浅拷贝,因此用 sp1 拷贝构造 sp2 后,相当于 sp1 和 sp2 管理了同一块内存空间,当 sp1 和 sp2 析构时就会导致这块空间被释放两次

  • 那么这里可以使用深拷贝吗?不可以,违背了功能的需求
  • 智能指针就是要模拟原生指针的行为,当我们将一个指针赋值给另一个指针时,目的就是让这两个指针指向同一块内存空间,所以这里本就应该进行浅拷贝,但单纯的浅拷贝又会导致空间被多次释放,因此根据解决智能指针拷贝问题方式的不同,从而衍生出了不同版本的智能指针

二. C++中的智能指针登场

🥑auto_ptr

💥 管理权转移:资源管理权转移,不负责任的拷贝,会导致被拷贝对象悬空

int main()
{auto_ptr<A> ap1(new A);ap1->_a1++;ap1->_a2++;auto_ptr<A> ap2(ap1);return 0;
}

在这里插入图片描述

但一个对象的管理权转移后也就意味着,该对象不能再用对原来管理的资源进行访问了,否则程序就会崩溃,因此使用auto_ptr之前必须先了解它的机制,否则程序很容易出问题,很多公司也都明确规定了禁止使用auto_ptr

😎auto_ptr的模拟实现

实现步骤如下:

  • 1️⃣在构造函数中获取资源,在析构函数中释放资源,利用对象的生命周期来控制资源(RAII)
  • 2️⃣对*->运算符进行重载,使auto_ptr对象具有指针一样的行为
  • 3️⃣在拷贝构造函数中,用传入对象管理的资源来构造当前对象,并将传入对象管理资源的指针置空
  • 4️⃣在拷贝赋值函数中,先将当前对象管理的资源释放,然后再接管传入对象管理的资源,最后将传入对象管理资源的指针ap置空
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; //管理权转移后,ap置空}auto_ptr<T>& operator=(auto_ptr<T>& ap){//检测是否为自己给自己赋值if (this != &ap){// 释放当前对象中资源if (_ptr)delete _ptr;//转移ap中资源到当前对象中_ptr = ap._ptr;ap._ptr = nullptr;}return *this;}~auto_ptr(){delete _ptr;}//像指针一样使用T& operator*()  //解引用{return *_ptr;}T* operator->() //自定义类型{return _ptr;}private:T* _ptr;
};

🥑unique_ptr

C++11中引入的智能指针,unique_ptr通过 防止拷贝(+ delete )的方式解决智能指针的拷贝问题

简单粗暴,不让拷贝
void test_unique_ptr()
{std::unique_ptr<A> up1(new A);//std::unique_ptr<A> up2(up1);//出错
}

但是总会有需要拷贝的场景吧

模拟实现如下:

  1. 在构造函数中获取资源,在析构函数中释放资源,利用对象的生命周期来控制资源
  2. *->运算符进行重载,使unique_ptr对象具有指针一样的行为
  3. 用C++98的方式将拷贝构造函数和拷贝赋值函数声明为私有(只声明不实现,或者用C++11的方式在这两个函数后面加上=delete,防止外部调用
template<class T>
class unique_ptr
{
public:unique_ptr(T* ptr = nullptr):_ptr(ptr){}//防止拷贝unique_ptr(unique_ptr<T>& ap) = delete;unique_ptr<T>& operator=(unique_ptr<T>& ap) = delete;~unique_ptr(){delete _ptr;}//像指针一样使用T& operator*()  //解引用{return *_ptr;}T* operator->() //自定义类型{return _ptr;}private:T* _ptr;
};

如果面试官要我们现场手撕,那我们就撕一个unique_ptr

🥑shared_ptr

🎨基本设计

是通过引用计数的方式来实现多个shared_ptr对象之间共享资源

  • 每一个被管理的资源都有一个对应的引用计数,通过这个引用计数记录着当前有多少个对象在管理着这块资源
  • 当新增一个对象管理这块资源时则将该资源对应的引用计数进行++,每个对象释放时,--计数
  • 最后一个析构的对象,释放资源

在这里插入图片描述

通过这种引用计数的方式就能支持多个对象一起管理某一个资源,也就是支持了智能指针的拷贝,并且只有当一个资源对应的引用计数减为0时才会释放资源,因此保证了同一个资源不会被释放多次

void test_shared_ptr()
{ljj::shared_ptr<A> sp1(new A);ljj::shared_ptr<A> sp2(sp1);sp1->_a1++;sp1->_a2++;std::cout << sp2->_a1 << ":" << sp2->_a2 << std::endl;//1 1sp2->_a1++;sp2->_a2++;std::cout << sp1->_a1 << ":" << sp1->_a2 << std::endl;//2 2
}

在这里插入图片描述

🎨模拟实现

shared_ptr的模拟实现步骤如下:

  • 1️⃣增加一个成员变量count,表示智能指针对象管理的资源对应的引用计数
  • 2️⃣在构造的时候获取资源,并将count设置为1, 说明只有一个新建的对象在管理
  • 3️⃣拷贝构造的时候,传入对象一起管理它管理的资源,同时将该资源对应的count++
  • 4️⃣在拷贝赋值函数中,先将当前对象管理的资源对应的引用计数--如果减为0则需要释放),然后再与传入对象一起管理它管理的资源,同时需要将该资源对应的引用计数++(看下图)
  • 5️⃣析构时候需要将count--,如果减为0,彻底释放资源
  • 6️⃣对*->运算符进行重载,使shared_ptr对象具有指针一样的行为

在这里插入图片描述

template<class T>
class shared_ptr
{
public://RAIIshared_ptr(T* ptr = nullptr):_ptr(ptr),_pCount(new int(1))  //构造的时候设为1{}//拷贝构造shared_ptr(shared_ptr<T>& sp):_ptr(sp._ptr),_pCount(sp._pCount)  //把引用指针给与,共同管理{(*_pCount)++;}// sp1 = sp5shared_ptr<T>& operator=(shared_ptr<T>& sp){//防止同一块资源之间赋值if (_ptr != sp._ptr){//sp的资源-- 并且判断是否要置空if ((*_pCount)-- == 0){cout << "delete: " << _ptr << endl;delete _ptr;delete _pt;}//一起管理新资源,++计数_ptr = sp._ptr;_pCount = sp._pCount;(*_pCount)++;return *this;}}~shared_ptr(){if (--(*_pCount) == 0 && _ptr){std::cout << "Delete" << _ptr << std::endl;delete _ptr;delete _pCount;}}//像指针一样使用T& operator*()  //解引用{return *_ptr;}T* operator->() //自定义类型{return _ptr;}private:T* _ptr;int* _pCount; // 引用计数//int count;
};

为什么引用计数要设计成指针?放在堆区

首先count不能设置成一个int类型的成员变量,这意味着每一个对象都有属于自己的count,如果多个对象要管理一个资源的时候,岂不是乱套了?

在这里插入图片描述

还有就是count不能定义成一个静态的成员变量,因为静态成员变量是所有类型对象共享的,所以无论什么类型的对象都是共用一个count

在这里插入图片描述

所以每个资源需要管理时,给构造函数,构造new一个引用计数指针,在堆区开辟一块空间用于存储其对应的引用计数,如果有其他对象也想要管理这个资源,那么除了将这个资源给它之外,还需要把这个引用计数也给它

在这里插入图片描述

🎨线程安全问题

后续补上

🎨定制删除器

当智能指针对象的生命周期结束时,所有的智能指针默认都是以delete的方式将资源释放,这是不太合适的,因为智能指针并不是只管理以new方式申请到的内存空间,智能指针管理的也可能是以new[]的方式申请到的空间,或管理的是一个文件指针

struct Node
{int _val;ljj::weak_ptr<Node> _next;ljj::weak_ptr<Node> _prev;~Node(){cout << "~Node" << endl;}
};
void test_shared_ptr2()
{std::shared_ptr<Node> n1(new Node);std::shared_ptr<Node> n1(new Node[5]);//errorstd::shared_ptr<FILE> sp2(fopen("test.cpp", "r")); //errorstd::shared_ptr<int> n3(new int[5]);//内置类型没问题
}

这里为什么内置类型可以通过,但是自定义类型就会报错呢? 涉及指针偏移

  • 对于内置类型,new实际在底层调用了malloc,调用delete最终还是会调用到free
  • 自定义类型(看下图)

在这里插入图片描述
不管它底层崩不崩,我们要匹配好:new[]的方式申请到的内存空间必须以delete[]的方式进行释放,而文件指针必须通过调用fclose函数进行释放

这时就需要用到定制删除器来控制释放资源的方式,C++标准库中的shared_ptr提供了如下构造函数:

template <class U, class D>
shared_ptr (U* p, D del);//构造函数

参数说明:

  • p:需要让智能指针管理的资源
  • del:删除器,这个删除器是一个可调用对象,比如函数指针、仿函数、lambda表达式以及被包装器包装后的可调用对象
//仿函数
template<class T>
struct DeleteArray
{void operator()(T* ptr){cout << "delete[]" << ptr << endl;delete[] ptr;}
};//定值删除器
void test_shared_ptr2()
{//仿函数对象std::shared_ptr<Node> n1(new Node);std::shared_ptr<Node> n2(new Node[5], DeleteArray<Node>());std::shared_ptr<int> n3(new int[5], DeleteArray<int>());//内置类型没问题//lambda对象std::shared_ptr<Node> l1(new Node);std::shared_ptr<Node> l2(new Node[5], [](Node* ptr) { delete[] ptr; });std::shared_ptr<int> l3(new int[5], [](int* ptr) { delete[] ptr; });std::shared_ptr<int> l4((int*)malloc(sizeof(12)), [](int* ptr) { free(ptr); });std::shared_ptr<FILE> l5(fopen("test.txt", "w"), [](FILE* ptr) { fclose(ptr); });
}

最好用的肯定是lambda对象

unique_ptr中却是在模板参数中给的,只能在模板参数中传类型,不能用lambda对象

在这里插入图片描述

int main()
{//不能用lambdastd::unique_ptr<Node> up(new Node[5]);//error//模板中传的是类型,不是传对象不能加(),std::unique_ptr<Node, DeleteArray<Node>> up(new Node[5]);
}

🎃模拟删除器的实现:

  • 要在当前模拟实现的shared_ptr的基础上支持定制删除器,就只能给shared_ptr类再增加一个模板参数,在构造shared_ptr对象时就需要指定删除器的类型。然后增加一个支持传入删除器的构造函数,在构造对象时将删除器保存下来,在需要释放资源的时候调用该删除器进行释放即可。最好在设置一个默认的删除器,如果用户定义shared_ptr对象时不传入删除器,则默认以delete的方式释放资源
namespace ljj
{//默认删除器template<class T>class Delete{void operator()(T* ptr){delete ptr;}};template<class T, class D = Delete<T>>class shared_ptr{public:void Release(){if (--(*_pCount) == 0 && _ptr){//cout << "Delete" << _ptr << endl;//delete _ptr;//D del;//del(_ptr);D()(_ptr);//无参构造对象,operator()去决定,是free还是delete等等delete _pCount;}}~shared_ptr(){Release();}private:T* _ptr;int* _pCount; // 引用计数};
}

🥑weak_ptr

💦shared_ptr的循环引用问题

shared_ptr的循环引用问题在一些特定的场景下才会产生。比如定义如下的结点类,并在结点类的析构函数中打印一句提示语句,便于判断结点是否正确释放

struct Node
{int _val;std::shared_ptr<Node> _next;std::shared_ptr<Node> _prev;~Node(){cout << "~Node" << endl;}
};

现在以new的方式在堆上构建两个结点,并将这两个结点连接起来,又为了防止抛异常我们把类型改成智能指针

struct Node
{int _val;std::shared_ptr<Node> _next;std::shared_ptr<Node> _prev;~Node(){cout << "~Node" << endl;}
};
int main()
{std::shared_ptr<Node> n1 (new Node);std::shared_ptr<Node> n2 (new Node);n1->_next = n2;  //智能指针和原生指针不能直接赋值n2->_prev = n1;//...return 0;
}

在这里插入图片描述

此时两个结点都没有被释放,但如果去掉连接结点时的两句代码中的任意一句,那么这两个结点就都能够正确释放,根本原因就是因为这两句连接结点的代码导致了循环引用

下面来一探究竟吧

循环引用导致资源未被释放的原因: 很绕!

  1. n2先析构,n1再析构,所以引用计数都变成了1
  2. 右边的节点释放取决于_next析构,_next什么时候析构取决于左边节点的析构(作为成员);
  3. 左边节点的析构取决于_prev的析构,prev什么时候析构取决于右边节点的析构右边的节点释放又取决于_next析构
    在这里插入图片描述

二者互相纠缠,对此shared_ptr也是无能为力,又要引进一员大将weak_ptr

💦weak_ptr解决循环引用

weak_ptr就是shared_ptr的小跟班,不是常规智能指针,没有RAII,不支持直接资源管理

  • weak_ptr主要用shared_ptr构造,用来解决引用问题:构造出来的 weak_ptr对象不参与资源释放管理,可以访问和修改资源,但不会增加这块资源对应的引用计数

解决方案:在引用计数的场景下,把节点中的_prev和_next改成weak_ptr就可以了

原理就是:node1->_next = node2;和node2->_prev = node1;时weak_ptr的_next和
_prev不会增加node1和node2的引用计数,引用计数都是1,就可以按先后顺序释放了

struct Node
{int _val;std::weak_ptr<Node> _next;std::weak_ptr<Node> _prev;~Node(){cout << "~Node" << endl;}
};//循环引用
void test_weak_ptr()
{std::shared_ptr<Node> n1 (new Node);std::shared_ptr<Node> n2 (new Node);cout << n1.use_count() << endl;cout << n2.use_count() << endl;n1->_next = n2;  //智能指针和原生指针不能直接赋值;所以next变成智能指针n2->_prev = n1;cout << n1.use_count() << endl;cout << n2.use_count() << endl;
}

通过use_count获取这两个资源对应的引用计数就会发现,在结点连接前后这两个资源对应的引用计数就是1,根本原因就是weak_ptr不会增加管理的资源对应的引用计数

🎃weak_ptr的模拟实现

在这里插入图片描述

  1. 提供一个无参的构造函数shared_ptr对象拷贝构造、weak_ptr对象拷贝赋值、shared_ptr对象拷贝赋值给weak_ptr

  2. *->运算符进行重载,使weak_ptr对象具有指针一样的行为

//辅助型智能指针,配合解决shared_ptr的循环引用问题
template<class T>
class weak_ptr
{
public:weak_ptr()    //无参:_ptr(nullptr){}weak_ptr(const weak_ptr<T>& wp)  //weak类型构造:_ptr(wp._ptr){}weak_ptr(const shared_ptr<T>& sp) //shared_ptr类型构造:_ptr(sp.get()){}weak_ptr<T>& operator= (const shared_ptr<T>& sp){_ptr = sp.get();return *this;}//像指针一样使用T& operator*(){return *_ptr;}T* operator->(){return _ptr;}private:T* _ptr;
};

注意: shared_ptr还会提供一个get函数,用于获取其管理的_ptr

C++11和boost中智能指针的关系

  1. C++98中产生了第一个智能指针auto_ptr
  2. C++boost给出了更实用的scoped_ptr、shared_ptr和weak_ptr
  3. C++TR1,引入了boost中的shared_ptr等。不过注意的是TR1并不是标准版
  4. C++11,引入了boost中的unique_ptr、shared_ptr和weak_ptr。需要注意的是,unique_ptr对应的就是boost中的scoped_ptr,并且这些智能指针的实现原理是参考boost中实现的

说明一下:boost库是为C++语言标准库提供扩展的一些C++程序库的总称,boost库社区建立的初衷之一就是为C++的标准化工作提供可供参考的实现,比如在送审C++标准库TR1中,就有十个boost库成为标准库的候选方案。

常见面试题

  1. 为什么需要智能指针? —— 忘记释放/异常安全
  2. RAII是什么?
  3. 发展历史
  4. auto_ptr/unique_ptr/shared_ptr/weak_ptr之间区别和使用场景
  5. 模拟实现简洁版的智能指针(没说就实现unique,不然就是shared的
  6. 什么是循环引用?如何解决循环引用?解决的原理是什么?

📢写在最后

请添加图片描述

相关文章:

【C++】智能指针(万字详解)

&#x1f308;欢迎来到C专栏~~智能指针 (꒪ꇴ꒪(꒪ꇴ꒪ )&#x1f423;,我是Scort目前状态&#xff1a;大三非科班啃C中&#x1f30d;博客主页&#xff1a;张小姐的猫~江湖背景快上车&#x1f698;&#xff0c;握好方向盘跟我有一起打天下嘞&#xff01;送给自己的一句鸡汤&…...

使用docker配置mysql主从复制

1.新建主服务器容器实例&#xff1a; docker run -p 3307:3306 --name mysql \ -v /docker/mysql/data:/var/lib/mysql \ -v /docker/mysql/conf:/etc/mysql/conf \ -v /docker/mysql/log:/var/log/mysql \ -e MYSQL_ROOT_PASSWORDroot \ -d mysql:5.7 设置容器卷之后&#xf…...

v3 异步组件及分包使用

1 app.vue <template> <!-- vue3异步组件必须使用suspense --> <Suspense> <template #default> <!-- 异步组件 --> <SyncVue></SyncVue> </template> <template v-slot:fallback> <!-- 优先显示骨架屏 --> <…...

实用调试技巧【上篇】

&#x1f534;本文章是在 Visual Studio 2022&#xff08;VS2022&#xff09;编译环境下进行操作讲解 文章目录&#x1f973;1. 什么是bug&#xff1f;&#x1f973;2.调试有多重要&#xff1f;2.1. 我们是如何写代码的&#xff1f;2.2.调试是什么&#xff1f;2.3.调试的基本步…...

JavaScript 教程

手册简介JavaScript 是世界上最流行的脚本语言。 JavaScript 是属于 web 的语言&#xff0c;它适用于 PC、笔记本电脑、平板电脑和移动电话。 JavaScript 被设计为向 HTML 页面增加交互性。 许多 HTML 开发者都不是程序员&#xff0c;但是 JavaScript 却拥有非常简单的语法。几…...

在SpringBoot里面使用原生的Servlet

在SpringBoot里面使用Servlet 首先在主程序中添加注解主程序添加ServletComponentScan // 加上这个注解之后就可以使用原生的组件了 HttpServlet 继承HttpServlet 重写方法 添加WebServlet 第一种方式使用注解 WebServlet(value "/helsk") public class HelloSe…...

商标被驳回,先别慌!挽回商标有办法

商标注册是一个漫长的等待过程&#xff0c;提交了注册申请之后不代表就能得心应手。商标局在接收到申请后&#xff0c;便会开始各阶段审查&#xff0c;面对不符合条件的商标会予以商标驳回。商标局基于什么原因而驳回注册申请呢?驳回后还有必要进行商标驳回复审吗?今天心周企…...

VMware安装Linux虚拟机后忘记root密码处理方法

OS版本&#xff1a;Red Hat 7.7 问题说明&#xff1a; 之前用VMWare安装了一台Linux虚机&#xff0c;由于长期没使用&#xff0c;导致忘记了root密码。所以需要修改root密码。 Root密码修改 现将修改root密码的操作步骤记录如下。 1.启动虚拟机&#xff0c;出现启动倒计时…...

Centos安装OpenResty

文章目录一. OpenResty是什么二. OpenResty的安装1. 安装开发库2. 安装OpenResty仓库3. 安装OpenResty4. 安装opm工具5. 目录结构6. 配置nginx的环境变量7. 启动和运行8. 配置文件修改三. 小案例1. 案例说明2. OpenResty监听请求3. 编写业务代码4. 获取请求参数一. OpenResty是…...

阿里云部署SpringBoot项目

目录 步骤1&#xff1a;购买服务器(新用户免费试用一个月) 步骤2&#xff1a;查看服务器相关信息 ​编辑 步骤3&#xff1a;设置安全组 步骤4&#xff1a;远程连接 步骤5&#xff1a;使用FinalShell连接阿里云服务器 步骤6&#xff1a;阿里云服务器上安装JDK ​编辑 步骤7…...

EdgeCOM嵌入式边缘计算机的参数配置

EdgeCOM嵌入式边缘计算机的参数配置&#xff1a; 下面以 eth0 为例进行命令说明。 在 Linux 系统下&#xff0c;使用 ifconfig 命令可以显示或配置网络设备&#xff0c;使用 ethtool 查询及 设置网卡参数。 设置 IP 地址&#xff0c;查看当前网卡详情&#xff1a; rootfl-imx6u…...

字节软件测试岗:惨不忍睹的三面,幸好做足了准备,月薪15k,拿到offer

我今年25岁&#xff0c;专业是电子信息工程本科&#xff0c;19年年末的时候去面试&#xff0c;统一投了测试的岗位&#xff0c;软件硬件都有&#xff0c;那时候面试的两家公司都是做培训的&#xff0c;当初没啥钱&#xff0c;他们以面试为谎言再推荐去培训这点让我特别难受。 …...

【编程基础之Python】5、安装Python第三方模块

【编程基础之Python】5、安装Python第三方模块安装Python第三方模块为什么需要安装第三方模块Python包管理器介绍pippip installpython -m pip installcondaconda install在Windows环境中安装Python模块安装numpy安装pandas安装matplotlib在Linux环境中安装Python模块在PyCharm…...

JavaScript 教程导读

JavaScript 是 Web 的编程语言。所有现代的 HTML 页面都使用 JavaScript&#xff0c;可以用于改进设计、验证表单、检测浏览器、创建cookies等。JavaScript 非常容易学。本教程将教你学习从初级到高级JavaScript知识。JavaScript 在线实例本教程包含了大量的 JavaScript 实例&a…...

BigDecimal

文章目录1. BigDecimal 的舍入模式&#xff08;RoundingMode&#xff09;1.1 ROUND_UP1.2 ROUND_DOWN1.3 ROUND_HALF_UP1.4 ROUND_HALF_DOWN1.5 ROUND_CEILING1.6 ROUND_FLOOR1.7 ROUND_HALF_EVEN1.8 ROUND_UNNECESSARY2. BigDecimal的运算——加减乘除2.1 加法 add()函数 减法…...

代码随想录【Day15】|102. 二叉树的层序遍历、226. 翻转二叉树、101. 对称二叉树

102. 二叉树的层序遍历 题目链接 题目描述&#xff1a; 给你一个二叉树&#xff0c;请你返回其按 层序遍历 得到的节点值。 &#xff08;即逐层地&#xff0c;从左到右访问所有节点&#xff09;。 难点&#xff1a; 思路&#xff1a; 需要借用一个辅助数据结构即队列来实现…...

Python学习笔记:快速上手:基础知识

快速上手&#xff1a;基础知识 数和表达式 除法 >>> 1 / 2 0.5 >>> 1 / 1 1.0整除 >>> 1 // 2 0 >>> 1 // 1 1 >>> 5.0 // 2.4 2.0求余&#xff08;求模&#xff09;&#xff1a; x % y 等价于x - ((x // y) * y)。 …...

excel学习笔记-导入外部文件,报错,数值格式变换,日期格式的转化,求和快捷键,冻结窗格

这里写目录标题一、导入外部文件1.导入csv文件2.导入txt文件3.修改txt内容&#xff0c;需要刷新才能看见更改二、报错三、数值格式变换四、日期格式的转化五、ALT &#xff0c;求和快捷键六、冻结窗格一、导入外部文件 1.导入csv文件 2.导入txt文件 3.修改txt内容&#xff0c;…...

06 OpenCV‘阈值处理、自适应处理与ostu方法

1 基本概念 CV2中使用阈值的作用是将灰度图像二值化&#xff0c;即将灰度图像的像素值根据一个设定的阈值分成黑白两部分。阈值处理可以用于图像分割、去除噪声、增强图像对比度等多个领域。例如&#xff0c;在物体检测和跟踪中&#xff0c;可以通过对图像进行阈值处理来提取目…...

月薪过万的那些人,大部分都是做什么工作的?

三百六十行&#xff0c;行行出状元。不管是什么行业&#xff0c;月薪过万都是有的。只不过有些行业就是比较容易出现月薪过万&#xff0c;换句话说&#xff0c;就是这个行业内出现月薪过万的人数比较多。先说结论&#xff0c;综合来看月薪过万的这部分90后&#xff0c;大部分集…...

csgo搬砖项目,门槛最低的副业就是它(内附入门知识及选品技巧)

CSGO搬砖如何选择游戏饰品(装备&#xff09;&#xff1f;相信很多朋友一定很关心这个问题&#xff0c;因为如何选品直接关系到该装备是否快速出售&#xff0c;而且也关系到账号整体盈收状况。那么今天阿阳就来好好聊聊如何选择Steam装备以及饰品的各项知识点。 Steam搬砖如何选…...

【闲聊杂谈】高并发下基于LVS的负载均衡

1、使用http协议进行网络请求 在前几年公布的用户入网数据中&#xff0c;移动入网的数量已经达到六七亿的规模&#xff0c;固网用户数也达到三至五个亿。想要解决这么大并发访问的场景&#xff0c;有多种的解决方案&#xff0c;常规有基于4层的&#xff0c;也有基于7层的。这个…...

Redis新数据类型

目录 Bitmaps 简介 命令 Bitmaps和set对比 HyperLogLog 介绍 命令 Geospatial 简介 命令 Bitmaps 简介 现代计算机用二进制(位)作为信息的基本单位&#xff0c;1个字节等于8位。合理的使用和操作位可以有效的提高内存的使用率和开发效率。 redis提供了Bitmaps这个"数据类…...

使用Python绘制股票CCI指标曲线

本文使用Python语言绘制一只股票的CCI&#xff08;Commodity channel index&#xff09;曲线&#xff0c;论文参考《Commodity channel index: Tool for trading cyclic trends》&#xff0c;该指标可以用来测量股价、外汇或者贵金属交易是否已超出常态分布范围&#xff0c;​ …...

【C语言技能树】浮点数在内存中的存储

Halo&#xff0c;这里是Ppeua。平时主要更新C语言&#xff0c;C&#xff0c;数据结构算法......感兴趣就关注我吧&#xff01;你定不会失望。 &#x1f308;个人主页&#xff1a;主页链接 &#x1f308;算法专栏&#xff1a;专栏链接 我会一直往里填充内容哒&#xff01; &…...

Spring框架源码(五) @configuration源码深度解析

Configuration 注解是spring-context模块提供的一个给开发者使用的配置类注解&#xff0c;开发者可以通过Configuration注解来定义配置类&#xff0c;也可以使用xml形式注入。 例如配置数据库配置&#xff0c;定义一个配置类&#xff0c;注入数据源DataSource, 事务管理器Trans…...

gcc/g++从入门到精通(3)gcc头文件、库搜索路径方式全面盘点

🎀 关于博主👇🏻👇🏻👇🏻 🥇 作者简介: 热衷于知识探索和分享的技术博主。 💂 csdn主页::【奇妙之二进制】 ✍️ 微信公众号:【Linux 世界】 🎉精彩专栏: 🎓 【面向工作git基础教程】 ​ 🧡 【C++11新特性深入剖析】 ​ 📚【shell脚本编程基础与...

Android Studio多渠道打包及自动化构建

Android 有不同的应用市场&#xff0c;也就是不同的渠道&#xff0c;需要为每个应用市场打一个安装包&#xff0c;但主要的代码是一样的&#xff0c;可能部分资源不一样&#xff0c;部分代码不一样&#xff0c;如果每个渠道都需要修改&#xff0c;然后打包&#xff0c;非常耗时…...

基于MATLAB的MIMO信道估计(附完整代码与分析)

目录 一. 介绍 二. MATLAB代码 三. 运行结果与分析 一. 介绍 本篇将在MATLAB的仿真环境中对比MIMO几种常见的信道估计方法的性能。 有关MIMO的介绍可看转至此篇博客&#xff1a; MIMO系统模型构建_唠嗑&#xff01;的博客-CSDN博客 在所有无线通信中&#xff0c;信号通过…...

Python代码游戏————星球大战

♥️作者:小刘在C站 ♥️个人主页:小刘主页 ♥️每天分享云计算网络运维课堂笔记,努力不一定有收获,但一定会有收获加油!一起努力,共赴美好人生! ♥️夕阳下,是最美的绽放,树高千尺,落叶归根人生不易,人间真情 目录 一.Python介绍 二.游戏效果呈现 三.主代码 四....