C++之异常智能指针其他

C++之异常&智能指针&其他

  • 异常
    • 关于函数异常声明
    • 异常的优劣
  • 智能指针
    • auto_ptr
    • unique_ptr
    • shared_ptr
    • weak_ptr
      • 定制删除器
    • 智能指针的历史与boost库
  • 特殊类
  • 单例模式
    • 饿汉和懒汉的优缺点
  • C++四种类型转换
  • C++IO流
  • 结语

异常

try括起来的的代码块中可能有throw一个异常(可以是任意类型)的操作,catch可以捕捉throw出来的异常。

  • throw出异常后,代码执行会直接跳转到catch的地方。可能引发内存泄漏、文件描述符忘关等严重问题。解决办法如下:
void func()
{int* x = new int;throw x;cout << "delete x" << endl;delete x;//走不到这里
}
int main()
{try{func();}catch (int* y){delete y;cout << "delete y" << endl;}
}
  • catch的类型只有和throw的类型匹配,才能被catch到。
  • 当有多个catch可以接收异常时,走最近的。比如先catch一个父类,再catch一个子类,抛出一个子类,那就会直接走父类的catch,因为切片可以接收,又是靠前的。
  • catch接收的其实是throw的原对象的临时拷贝,与函数传值返回类似,拷贝出来的临时对象在catch接收后销毁,而原对象出作用域销毁。
  • catch(…)可以捕获任意异常。通常加在最后,捕获后,程序可以继续运行,代码健壮性提升。
  • 通常会在最外层统一捕获、处理异常。所以中间层通常会捕获异常后再抛出去。

下面展示一个异常机制的常见用法:

class Exception
{
public:Exception(int id, const string& msg):_errid(id), _errmsg(msg){}int getId() { return _errid; }string getmsg() { return _errmsg; }virtual string what()const { return _errmsg; }
protected:string _errmsg;int _errid;
};
class SqlException :public Exception
{
public:SqlException(int id, const string& msg):Exception(id, msg){}virtual string what()const{return "SqlException:" + _errmsg;}
};
class NetworkException :public Exception
{
public:NetworkException(int id, const string& msg):Exception(id, msg){}virtual string what()const{return "NetworkException:" + _errmsg;}
};
class HttpException :public Exception
{
public:HttpException(int id, const string& msg, const string& type):Exception(id, msg), _type(type){}virtual string what()const{return "HttpException:" + _type + _errmsg;}
private:string _type;
};
int main()
{try{//run();SqlException se(1, "sqlwrong");throw(se);}catch (const Exception& e)//配合多态{cout << e.what() << endl;}catch (...){cout << "Unknown Exception" << endl;}
}

关于函数异常声明

C++98支持在函数声明后加上throw(),括号里是可能抛出的异常的类型,throw(A,B,C)表示可能抛出A/B/C中某个类型的异常,但如果抛了别的类型,也不会报错,而且不强制让加这个声明,太自由了,没什么人遵守。
C++11支持在函数声明后加上noexcept,如果这个函数不抛异常的话。而且如果它抛异常,哪怕是它本身不抛异常但内部嵌套了一个抛异常的函数,程序执行也会终止。所以noexcept关键字也算好用,但是也不强制写。
异常声明

异常的优劣

异常机制的优点:

  1. 相比于c语言的错误码方式,可以增添错误信息,帮助定位bug。
  2. 对于多层函数调用的错误码要作为返回值层层传递,较为繁琐,而抛异常则直接跳到最外层捕获异常。
  3. 一些函数只能抛异常来终止程序,比如构造里面new失败。

异常机制的缺点:

  1. 抛异常会导致执行流乱跳,不利于调试分析。
  2. C++没有垃圾回收机制,容易导致内存泄漏、死锁及文件描述符泄露等问题。
  3. C++标准库的异常体系定义的不好,大家都搞了自己的一套,混乱。

总结:异常机制利大于弊,推荐使用,而且要规范使用,比如加上noexcept。
但是呢,针对上面提到的内存泄漏,我们也提出了解决方案,也就是下文的RAII风格的智能指针。

智能指针

RAII(Resource Acquisition Is Initialization,译资源获取即初始化),是一种利用对象生命周期来控制、管理资源的设计风格。具体是,对象构造时获取资源,析构时释放资源,全自动的管控。
智能指针原型smartptr的模拟实现:

//智能指针,支持RAII,可以像指针一样使用
template<class T>
class SmartPtr
{
public:SmartPtr(T*ptr):_ptr(ptr){}T& operator*() { return *_ptr; }T* operator->() { return _ptr; }~SmartPtr() { delete _ptr; }
private:T* _ptr;
};

这样可以防止new后面程序抛异常跳过delete阶段,因为智能指针出作用域析构自动释放指针。


但是,新问题来了,如果有人拷贝了这个智能指针,那它们都指向同一块资源,delete两次会程序崩溃,如何解决?
有人说深拷贝一个不就好了。但是智能指针就是要像指针一样玩,指针拷贝也就是赋值,就是浅拷贝,指向同一块资源,所以排除。

auto_ptr

这时,有大佬提出了一个解决方案,资源管理权转移,也就是auto_ptr的方案:

namespace lky
{template<class T>class auto_ptr{public:auto_ptr(T* ptr) :_ptr(ptr) {}//管理权转移auto_ptr(auto_ptr& ap){_ptr = ap._ptr;ap._ptr = nullptr;}T& operator*() { return *_ptr; }T* operator->() { return _ptr; }~auto_ptr() { delete _ptr; }//delete/free nullptr是没问题的,不放心也可以检查😂private:T* _ptr;};void test_auto(){auto_ptr<int>ap1 = new int(1);auto_ptr<int>ap2(ap1);*ap1 = 2;//管理权转移后,ap1悬空,不能被访问,库里的auto_ptr也是如此*ap2 = 2;}
}

这是C++98就被纳入库中的auto_ptr方案。对于不熟悉它的特性的人去使用它,他是不知道被拷贝的对象悬空的,这时还去使用就崩,这样一个“坑货”就被纳进了标准库。。。它被骂了很多年,很多公司明确规定不能使用auto_ptr。后来,C++11才收录了unique_ptr,shared_ptr和weak_ptr。为什么叫收录呢,在后面boost库再讲。

unique_ptr

实现思路很简单,禁掉拷贝构造还有赋值即可。也就是要么在private声明不实现,要么=delete关键字禁掉。

namespace lky
{template<class T>class unique_ptr{public:unique_ptr(T* ptr) :_ptr(ptr) {}unique_ptr(const unique_ptr&) = delete;unique_ptr& operator=(const unique_ptr&) = delete;T& operator*() { return *_ptr; }T* operator->() { return _ptr; }~unique_ptr() { delete _ptr; }private:T* _ptr;};void test_unique(){unique_ptr<int>up1 = new int(1);unique_ptr<int>up2(up1);//会报错,无法引用已删除的函数}
}

平常不需要指针拷贝的场景,用unique_ptr就行。
但是,如果非要指针拷贝呢?

shared_ptr

于是乎,有了后面的share_ptr通过一个引用计数解决:

namespace lky
{template<class T>class shared_ptr{public:shared_ptr(T* ptr = nullptr):_ptr(ptr), _pcount(new int(1)), _pmtx(new mutex){}shared_ptr(const shared_ptr<T>& sp):_ptr(sp._ptr), _pcount(sp._pcount), _pmtx(sp._pmtx){AddCount();}void Release(){_pmtx->lock();bool DeleteLock = false;if (--*_pcount == 0){if (_ptr){cout << "delete:" << _ptr << endl;delete _ptr;}delete _pcount;DeleteLock = true;}_pmtx->unlock();if (DeleteLock){delete _pmtx;}}void AddCount(){_pmtx->lock();++*_pcount;_pmtx->unlock();}T* get(){return _ptr;}int use_count(){return *_pcount;}shared_ptr<T>& operator=(const shared_ptr<T>& sp){//if (&sp != this)//防不完全,管同一块资源的也要防if (_ptr != sp._ptr){Release();_ptr = sp._ptr;_pcount = sp._pcount;_pmtx = sp._pmtx;AddCount();}return *this;}T& operator*(){return *_ptr;}T* operator->(){return _ptr;}~shared_ptr(){Release();}private:T* _ptr;int* _pcount;mutex* _pmtx;};void test_shared_ptr(){lky::shared_ptr<int>sp1(new int(1));lky::shared_ptr<int>sp2(new int(777));lky::shared_ptr<int>sp3(sp1);lky::shared_ptr<int>sp4(sp1);sp4 = sp1;sp3 = sp2;}struct Date{int _year = 0;int _month = 0;int _day = 0;};void isSharedSafe(shared_ptr<Date>& sp, size_t N, mutex& mtx){//cout << sp.get() << endl;for (size_t i = 0; i < N; i++){lky::shared_ptr<Date>copy(sp);sp->_year++;sp->_month++;sp->_day++;}}void test_shared_ptr_safe(){lky::shared_ptr<Date>sp(new Date);//cout << sp.get() << endl;const size_t N = 100000;mutex mtx;thread t1([&]() {//cout << sp.get() << endl;for (size_t i = 0; i < N; i++){lky::shared_ptr<Date>copy(sp);sp->_year++;sp->_month++;sp->_day++;}});//线程传参,默认传值传参,想传引用就用库函数ref。thread t2(isSharedSafe, std::ref(sp), N, std::ref(mtx));//这里因为在调thread的构造,相当于封装了一层。传引用断层了t1.join();t2.join();cout << "count:" << sp.use_count() << endl;//恒为1,线程安全cout << "y:" << sp->_year << endl;cout << "m:" << sp->_month << endl;cout << "d:" << sp->_day << endl;}struct ListNode{lky::shared_ptr<ListNode> _prev;lky::shared_ptr<ListNode> _next;int _val;~ListNode() { cout << "~ListNode()" << endl; }};//当shared_ptr遇到循环引用问题...void test_shared_ptr_cycle(){ListNode ln1;ListNode ln2;lky::shared_ptr<ListNode>sp1(&ln1);lky::shared_ptr<ListNode>sp2(&ln2);sp1->_next = sp2;sp2->_prev = sp1;//可以看到,没有调用ListNode的析构,如果注释此行,就有了}
}
int main()
{//lky::test_shared_ptr();//lky::test_shared_ptr_safe();//可以看到shared_ptr的引用计数操作因为加锁保护,是线程安全的//但是shared_ptr管理的对象操作不一定线程安全lky::test_shared_ptr_cycle();
}

首先,shared_ptr本身是线程安全的,包括引用计数++ - -,但是他管理的对象可能没加锁或不是原子的之类,导致对象操作不一定线程安全。
关于循环引用问题:
循环引用

  1. 当栈上的sp2先析构,Release让LN2的引用计数减到1,然后sp1再析构,又让LN1的引用计数减到1,就结束了。没错,堆上动态开辟的可空间我们并没有手动释放,os更不会管(但os会在程序结束后将空间资源全部回收),自然没有调用ListNode的析构。
  2. 而且,就算不在堆上,在栈上,释放完sp1和sp2,该释放LN2,先调它的_next一个nullptr的析构,然后_prev的析构让LN1的引用计数减到0,LN1释放,先调它的_next的析构让LN2的引用计数减到0,就再次进入LN2的析构,就再次先调_next的析构,这时就产生两次加锁导致的死锁问题了,程序崩溃。当然了,假设没锁,让引用计数一直陪它们减减,它们互相牵制,最后也是谁也释放不了。

于是,为了解决循环引用问题,有了weak_ptr。

weak_ptr

weak_ptr的基本思路很简单,指向同一块资源,不增加引用计数即可。
如下:

namespace lky
{//记得把shared_ptr的get给上consttemplate<class T>class weak_ptr{public:weak_ptr():_ptr(nullptr){}weak_ptr(const shared_ptr<T>& sp):_ptr(sp.get()){}T& operator*(){return *_ptr;}T* operator->(){return _ptr;}private:T* _ptr;};struct ListNode{lky::weak_ptr<ListNode> _prev;lky::weak_ptr<ListNode> _next;int _val;~ListNode() { cout << "~ListNode()" << endl; }};//当shared_ptr遇到循环引用问题...void test_weak_ptr_cycle(){lky::weak_ptr<ListNode>wp1(new ListNode);//单参数隐式类型转换lky::weak_ptr<ListNode>wp2(new ListNode);wp1->_next = wp2;wp2->_prev = wp1;//可以看到,没有调用ListNode的析构,如果注释此行,就有了}//weak_ptr不支持RAII,但支持像指针一样使用,专门辅助shared_ptr解决循环引用
}
int main()
{lky::test_weak_ptr_cycle();
}

可以看到,weak_ptr不支持RAII,但支持像指针一样使用,专门辅助shared_ptr解决循环引用。它可以指向资源,但不参与管理,不增加引用计数。
库中实现的复杂的多,大几千行工程级代码,考虑了诸如过期(资源已经释放,但仍指向)等问题,还要全面配合shared_ptr。

定制删除器

其实就是一种可调用对象。因为智能指针可能指向自定义类型数组,需要delete[ ],而析构时默认使用delete,就会引发问题(前面讲过释放位置不对),于是需要外界传一个可调用对象来自定义删除动作。
shared_ptr和unique_ptr的构造都有允许传定制删除器的函数:定制删除器
以unique_ptr为例,模拟实现如下:

namespace lky
{template<class T>class unique_ptr{public:unique_ptr(T* ptr) :_ptr(ptr) {}template<class D>unique_ptr(T* ptr, D del) : _ptr(ptr), _del(del) {}unique_ptr(const unique_ptr&) = delete;unique_ptr& operator=(const unique_ptr&) = delete;T& operator*() { return *_ptr; }T* operator->() { return _ptr; }~unique_ptr(){//delete _ptr; _del(_ptr);}private:T* _ptr;function<void(T*)>_del = [](T* ptr) {cout << "delete:" << ptr << endl;delete ptr; };};void test_unique_del(){unique_ptr<int>up1 = new int(1);unique_ptr<int>up2(new int[10], [](int* ptr) {cout << "delete[]:" << ptr << endl;delete[] ptr;});}
}
int main()
{lky::test_unique_del();
}

用function来包装接收可调用对象。库里实现不太一样,但功能一样。

智能指针的历史与boost库

  1. C++98有了第一个智能指针auto_ptr
  2. boost库搞出了scoped_ptr,shared_ptr和weak_ptr。boost是C++标准委员会成员搭建的准标准库,类似抢先服,很多好用的东西都被C++标准库引进吸收。
  3. C++11引入unique_ptr(对应scoped_ptr),shared_ptr和weak_ptr。

特殊类

  1. 只能在堆上创建的类
    法一,封构造,还需禁拷贝赋值
class HeapOnly
{
public:static HeapOnly* CreateObj(){HeapOnly* hp = new HeapOnly;return hp;}HeapOnly(const HeapOnly&) = delete;HeapOnly& operator=(const HeapOnly&) = delete;
private:HeapOnly() {}
};

法二,封析构

class HeapOnly
{
public:void Destroy(){delete this;}
private:~HeapOnly() {}
};
  1. 只能在栈上创建的类
class StackOnly
{
public:static StackOnly CreateObj(){return StackOnly();}StackOnly(const StackOnly&) { cout << "StackOnly(const StackOnly)" << endl;}StackOnly(const StackOnly&) = delete;StackOnly(StackOnly&&){}//法二:将 new 和 delete 声明为 private/*void* operator new(size_t) = delete;void* operator new[](size_t) = delete;void operator delete(void*) = delete;void operator delete[](void*) = delete;*/
private:StackOnly() { cout << "StackOnly()" << endl; }
};
int main()
{StackOnly so1 = StackOnly::CreateObj();//StackOnly* so2 = new StackOnly(so1);//堆上可以禁掉static StackOnly so2 = StackOnly::CreateObj();//static禁不掉
}

注意,这里确切来说,静态区的static对象禁不掉的。

单例模式

设计模式,可以理解为大佬们做出大量工程,总结经验后得出的代码最佳设计方式。
单例模式,适用于一个进程中只有一份实例的类。除此之外,还有工厂模式、观察者模式,自行了解。
单例模式分为饿汉模式和懒汉模式,下为饿汉模式:

class Singleton//饿汉模式:main函数之前就创建对象
{
public:static Singleton* GetInstance(){return _ins;}void Push(int x){_v.push_back(x);}void Print(){for (auto& x : _v) { cout << x << " "; }cout << endl;}
private:Singleton(){}Singleton(const Singleton&) = delete;Singleton& operator=(const Singleton&) = delete;
private:static Singleton* _ins;vector<int>_v;//假设存储全局都要用的只此一份的名单
};
Singleton* Singleton::_ins = new Singleton;//static成员定义可以使用成员函数

饿汉线程安全,main函数之前都没有多线程。
下为懒汉模式:

#include<mutex>
class Singleton//懒汉模式:第一次获取实例对象才创建
{
public:static Singleton* GetInstance(){	//双检查加锁if (_ins == nullptr)//提高效率,防止频繁上锁{_mtx.lock();if (_ins == nullptr){_ins = new Singleton;}_mtx.unlock();}return _ins;}void Push(int x){_v.push_back(x);}void Print(){for (auto& x : _v) { cout << x << " "; }cout << endl;}static void DelInstance(){_mtx.lock();if (_ins){delete _ins;}_mtx.unlock();}class GC//GC析构时自动delete单例,防止忘记DelInstance{public:~GC() { DelInstance(); }};static GC gc;~Singleton(){//...//持久化,程序结束时把数据写到文件里}
private:Singleton() {}Singleton(const Singleton&) = delete;Singleton& operator=(const Singleton&) = delete;private:static Singleton* _ins;vector<int>_v;//假设存储全局都要用的只此一份的名单static mutex _mtx;
};
Singleton* Singleton::_ins = nullptr;
mutex Singleton::_mtx;
Singleton::GC Singleton::gc;
int main()
{Singleton* st = Singleton::GetInstance();st->Push(1);st->Print();
}

懒汉需要加锁保证线程安全。
一般情况下,单例不需要手动释放,因为单例全局都要用,最后被os自动回收即可,但如果有提前释放需求,推荐内部类GC。
懒汉2.0:封掉构造,在GetInstance里定义static的单例对象,也不需要加锁。但是C++11之后静态局部变量的初始化才是线程安全的。

饿汉和懒汉的优缺点

饿汉:优点,比懒汉简单;缺点,创建出来时不需要使用却占用资源,拖慢程序启动速度(单例对象创建可能要IO等),并且无法保证多个单例之间的创建顺序。
懒汉:优点就是没有上面的缺点,缺点就是复杂点。

C++四种类型转换

为了规范c语言强制类型转换的安全清晰使用、避免隐式类型转换引发的问题如精度丢失等,C++提出了四种类型转换,如下:

  1. static_cast:用于类型相近的类型转换,比如double a=1.23;int b=static_cast<int>(a);
  2. reinterpret_cast:用于类型差别较大的类型转换,比如int a=0;int*p=reinterpret_cast<int*>(a);
  3. const_cast:用于去掉const属性。
const int a = 0; 
int* p = const_cast<int*>(&a);
*p = 3;
cout << a << endl;//还是0
cout << *p << endl;//3

a还是0,因为编译器做优化,可能把a的值放进寄存器,也可能把const属性的a进行类似宏替换处理。加上volatile关键字防止编译器优化即可。volatile

  1. dynamic_cast:用于将父类指针/引用转为子类指针/引用。
  • 首先,由于子类可能比父类多成员,父类强转子类后就可能访问越界。但是对于本来就是子类切片得来的父类,将其转回子类,是没问题的
  • dynamic_cast只能用于父类有虚函数的类。dynamic就在暗示你包含多态,这与它的底层实现有关,也就是虚表有关。
  • dynamic_cast先检查能否转成,能成则成,否则返回0。这也是它安全的原因。
  • 注意,父类对象肯定是永远无法转为子类对象的。
  • 切片/赋值兼容:子切出来父的那一部分,天然支持的,无论对象、指针还是引用。注意区别。
class A
{
public:virtual void func(){}//必要的int _a = 1;
};
class B :public A
{
public:int _b = 2;
};
int main()
{A* pa = new A;//B* pb = (B*)pa;//不安全,可能越界B* pb = dynamic_cast<B*>(pa);//安全,不可能越界/*cout << pa << endl;cout << pb << endl;if (pb)//被置空,因为可能越界{cout << pb->_a << endl;cout << pb->_b << endl;//强转的pb打出来的是随机值,越界了}*///对于可以成功dynamic_cast的情况A* pa2 = new B;//B* pb2 = (B*)pa2;//不安全,可能越界B* pb2 = dynamic_cast<B*>(pa2);//安全,不可能越界cout << pa2 << endl;cout << pb2 << endl;if (pb2)//成功转换,因为整体本身就是个子类对象{cout << pb2->_a << endl;cout << pb2->_b << endl;}
}

C++IO流

相比c语言的IO操作,C++的面向对象,且更好支持自定义类型的IO。
关于IO操作,这里只提关键点,其余自行查找资料。
首先库里常用的ostream,istream对标c的printf,scanf,iostream继承前两者,功能兼具。后面同理,一个文件流,一个字符串流。最常用的还是<<和>>以及getline。
ios
值得一提的是while(cin>>x){cout<<x;}>>返回的明明是istream对象引用,为什么可以转成bool判断真假?
operator bool
就是因为operator bool这个函数重载,支持从istream隐式类型转换转为bool。同理,operator int支持该对象转为int。这样就支持自定义类型转为内置类型
还有一点,二进制读写,读写对象中不能有string,因为读写的其实是string的str指针和size和capacity,那程序结束指针就销毁了,再读就野指针了。

结语

OK,C++学习博客到此为止,完结撒花!Linux篇再见🥰😘

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

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

相关文章

Ubuntu 20.04 Server版连接Wifi

前言 有时候没有网线口插网线或者摆放电脑位置不够时&#xff0c;需要用Wifi联网。以下记录Wifi联网过程。 环境&#xff1a;Ubuntu 20.04 Server版&#xff0c;无UI界面 以下操作均为root用户&#xff0c;如果是普通用户&#xff0c;请切换到root用户&#xff0c;或者在需要权…

Java项目实战II基于微信小程序的亿家旺生鲜云订单零售系统的设计与实现(开发文档+数据库+源码)

目录 一、前言 二、技术介绍 三、系统实现 四、核心代码 五、源码获取 全栈码农以及毕业设计实战开发&#xff0c;CSDN平台Java领域新星创作者&#xff0c;专注于大学生项目实战开发、讲解和毕业答疑辅导。获取源码联系方式请查看文末 一、前言 随着移动互联网技术的不断…

Javaweb梳理21——Servlet

Javaweb梳理21——Servlet 21 Servlet21.1 简介21.3 执行流程21.4 生命周期4.5 方法介绍21.6 体系结构21.7 urlPattern配置21.8 XML配置 21 Servlet 21.1 简介 Servlet是JavaWeb最为核心的内容&#xff0c;它是Java提供的一门动态web资源开发技术。使用Servlet就可以实现&…

MySQL 主从同步一致性详解

MySQL主从同步是一种数据复制技术&#xff0c;它允许数据从一个数据库服务器&#xff08;主服务器&#xff09;自动同步到一个或多个数据库服务器&#xff08;从服务器&#xff09;。这种技术主要用于实现读写分离、提升数据库性能、容灾恢复以及数据冗余备份等目的。下面将详细…

Unity-Particle System属性介绍(一)基本属性

什么是ParticleSystem 粒子系统是Unity中用于模拟大量粒子的行为的组件。每个粒子都有一个生命周期&#xff0c;包括出生、运动、颜色变化、大小变化和死亡等。粒子系统可以用来创建烟雾、火焰、水、雨、雪、尘埃、闪电和其他各种视觉效果。 开始 在项目文件下创建一个Vfx文件…

.NET8/.NETCore 依赖注入:自动注入项目中所有接口和自定义类

.NET8/.NETCore 依赖接口注入&#xff1a;自动注入项目中所有接口和自定义类 目录 自定义依赖接口扩展类&#xff1a;HostExtensions AddInjectionServices方法GlobalAssemblies 全局静态类测试 自定义依赖接口 需要依赖注入的类必须实现以下接口。 C# /// <summary>…

使用pyQT完成简单登录界面

import sysfrom PyQt6.QtGui import QMovie,QPixmap from PyQt6.QtWidgets import QApplication, QWidget, QLabel, QPushButton,QLineEdit#封装我的窗口类 class MyWidget(QWidget):#构造函数def __init__(self):#初始化父类super().__init__()# 设置窗口大小self.resize(330,…

linux安装部署mysql资料

安装虚拟机 等待检查完成 选择中文 软件选择 网络和主机名 开始安装 设置root密码 ADH-password 创建用户 等待安装完成 重启 接受许可证 Centos 7 64安装完成 安装mysql开始 Putty连接指定服务器 在 opt目录下新建download目录 将mysql文件传到该目录下 查看linux服务器的…

vscode 怎么下载 vsix 文件?

参考&#xff1a;https://marketplace.visualstudio.com/items?itemNameMarsCode.marscode-extension 更好的办法&#xff1a;直接去相关插件的 github repo 下载老版本 https://github.com/VSCodeVim/Vim/releases?page5 或者&#xff0c;去 open-vsx.org 下载老版本 点击这…

医院管理系统

私信我获取源码和万字论文&#xff0c;制作不易&#xff0c;感谢点赞支持。 医院管理系统 摘要 随着信息互联网信息的飞速发展&#xff0c;医院也在创建着属于自己的管理系统。本文介绍了医院管理系统的开发全过程。通过分析企业对于医院管理系统的需求&#xff0c;创建了一个计…

AWS账号提额

Lightsail提额 控制台右上角&#xff0c;用户名点开&#xff0c;选择Service Quotas 在导航栏中AWS服务中找到lightsail点进去 在搜索框搜索instance找到相应的实例类型申请配额 4.根据自己的需求选择要提额的地区 5.根据需求来提升配额数量,提升小额配额等大约1小时生效 Ligh…

Day52 | 动态规划 :单调栈 每日温度下一个更大的元素I下一个更大元素II

Day52 | 动态规划 &#xff1a;单调栈 每日温度&&下一个更大的元素I&&下一个更大元素II 单调栈【基础算法精讲 26】_哔哩哔哩_bilibili 及时去掉无用数据&#xff0c;保证栈中元素有序 文章目录 Day52 | 动态规划 &#xff1a;单调栈 每日温度&&下一…

第30天:安全开发-JS 应用NodeJS 指南原型链污染Express 框架功能实现审计0

时间轴&#xff1a; 演示案例&#xff1a; 环境搭建-NodeJS-解析安装&库安装 功能实现-NodeJS-数据库&文件&执行 安全问题-NodeJS-注入&RCE&原型链 案例分析-NodeJS-CTF 题目&源码审计 开发指南-NodeJS-安全 SecGuide 项目、 环境搭建-NodeJ…

Unity中的数学应用 之 插值函数处理角色朝向 (初中难度 +Matlab)

CodeMonkey教程&#xff1a; https://www.youtube.com/watch?vQDWlGOocKm8 Siki学院汉化教程&#xff1a;如何使用Unity开发分手厨房&#xff08;胡闹厨房&#xff09;-Unity2023 - SiKi学院|SiKi学堂 - unity|u3d|虚幻|ue4/5|java|python|人工智能|视频教程|在线课程 版本&am…

SQL注入利用方式(实战Hack World 1)

一、布尔盲注利用 假如注入的网页能返回1或0的提示信息&#xff0c;我们可以写如下代码: select password from admin where username 1 or 11;#11是我们利用的逻辑点&#xff0c;我们能在此处进行一个判断&#xff0c;比如判断某个数据字段第几位上的字符是否为’ 1’&#…

nlp培训重点

SGD梯度下降公式&#xff1a; 当梯度大于0时&#xff0c;变小&#xff0c;往左边找梯度接近0的值。 当梯度小于0时&#xff0c;减去一个负数会变大&#xff0c;往右边找梯度接近0的值&#xff0c;此时梯度从负数到0上升 #coding:utf8import torch import torch.nn as nn impo…

38 基于单片机的宠物喂食(ESP8266、红外、电机)

目录 一、主要功能 二、硬件资源 三、程序编程 四、实现现象 一、主要功能 基于STC89C52单片机&#xff0c;采用L298N驱动连接P2.3和P2.4口进行电机驱动&#xff0c; 然后串口连接P3.0和P3.1模拟ESP8266&#xff0c; 红外传感器连接ADC0832数模转换器连接单片机的P1.0~P1.…

Python 【图像分类】之 PyTorch 进行猫狗分类功能的实现(Swanlab训练可视化/ Gradio 实现猫狗分类 Demo)

Python 【图像分类】之 PyTorch 进行猫狗分类功能的实现(Swanlab训练可视化/ Gradio 实现猫狗分类 Demo) 目录 Python 【图像分类】之 PyTorch 进行猫狗分类功能的实现(Swanlab训练可视化/ Gradio 实现猫狗分类 Demo) 一、简单介绍 二、PyTorch 三、CNN 1、神经网络 2、卷…

week 6 - SQL Select II

Overview 1. Joins 包括交叉连接&#xff08;Cross&#xff09;、内连接&#xff08;Inner&#xff09;、自然连接&#xff08;Natural&#xff09;、外连接&#xff08;Outer&#xff09; 2. ORDER BY to produce ordered output 3. 聚合函数&#xff08;Aggregate Functio…

算法训练营day23(二叉树09:修建二叉搜索树,有序数组转化为平衡二叉搜索树,二叉搜索树转化为累加树,二叉树专题总结)

第六章 二叉树part09今日内容&#xff1a;● 669. 修剪二叉搜索树 ● 108.将有序数组转换为二叉搜索树 ● 538.把二叉搜索树转换为累加树 ● 总结篇 详细布置 669. 修剪二叉搜索树 这道题目比较难&#xff0c;比 添加增加和删除节点难的多&#xff0c;建议先看视频理解。题目…