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.rhkb.cn/news/482885.html

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

相关文章

混沌工程/混沌测试/云原生测试/云平台测试

背景 私有云/公有云/混合云等具有复杂&#xff0c;分布式&#xff0c;环境多样性等特点&#xff0c;许多特殊场景引发的线上问题很难被有效发现。所以需要引入混沌工程&#xff0c;建立对系统抵御生产环境中失控条件的能力以及信心&#xff0c;提高系统面对未知风险得能力。 …

Hive学习基本概念

基本概念 hive是什么&#xff1f; Facebook 开源&#xff0c;用于解决海量结构化日志的数据统计。 基于Hadoop的一个数据仓库工具&#xff0c;可以将结构化的数据文件映射为一张表&#xff0c;并提供类SQL查询功能 本质是将HQL转化为MapReduce程序。 Hive处理的数据存储在H…

数据分析流程中的Lambda架构,以及数据湖基于Hadoop、Spark的实现

文章目录 一、Lambda架构1、Lambda的三层架构2、简单解释&#xff1a;3、Lambda架构的优缺点 二、数据湖基于Hadoop、Spark的实现1、架构2、数据管理&#xff08;存储层的辅助功能&#xff09; 一、Lambda架构 1、Lambda的三层架构 Batch View&#xff08;批处理视图层&#…

算法笔记:力扣142.环形链表返回链表入口

该题目通俗来说就是需要返回节点的入口&#xff0c;这点与判断是否有环不同&#xff0c;有环是通过快慢指针的形式来判断&#xff0c;但当快慢指针相等的时候&#xff0c;此时的节点不一定是环的入口节点。所以这题需要注意。 关键API&#xff1a; map.putIfAbsent(key,value)…

医院管理系统

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

说说Elasticsearch查询语句如何提升权重?

大家好&#xff0c;我是锋哥。今天分享关于【说说Elasticsearch查询语句如何提升权重&#xff1f;】面试题。希望对大家有帮助&#xff1b; 说说Elasticsearch查询语句如何提升权重&#xff1f; 1000道 互联网大厂Java工程师 精选面试题-Java资源分享网 在 Elasticsearch 中&…

【Spring Security框架解析】

文章目录 Spring-security介绍Spring-security认证授权流程认证流程Security流程认证过滤器实现获取UserDetail信息 配置Security Spring-security介绍 Spring Security是一个功能强大且高度可定制的Java安全框架&#xff0c;用于保护基于Spring的应用程序。它提供了全面的安全…

[CISCN 2019华东南]Web11

[CISCN 2019华东南]Web11 给了两个链接但是都无法访问 这里我们直接抓包试一下 我们插入X-Forwarded-For:127.0.0.1 发现可以修改了右上角的IP地址&#xff0c;从而可以进行注入 {$smarty.version} 查看版本号 if标签执行PHP命令 {if phpinfo()}{/if} 查看协议 {if system(…

使用SpringBoot实现邮件发送(QQ邮箱为例)

使用SpringBoot实现邮件发送(QQ邮箱为例) 一、获取授权码 1.首先进入qq邮箱找到设置 2、账号栏目&#xff0c;找到POP3/SMTP服务 并开启服务 3、获取授权码 二、SpringBoot集成邮件发送 1.创建邮件发送服务类 package com.example.demo.service;import org.springframework…

hint: Updates were rejected because the tip of your current branch is behind!

问题 本地仓库往远段仓库推代码时候提示&#xff1a; error: failed to push some refs to 192.168.2.1:java-base/java-cloud.git hint: Updates were rejected because the tip of your current branch is behind! refs/heads/master:refs/heads/master [rejected] (…

基于BM1684的AI边缘服务器-模型转换,大模型一体机(二)

目标追踪 注&#xff1a;所有模型转换都是在docker环境中的 先进入docker 这里我们是要在docker环境里编译的&#xff0c;所以先进入docker :~/tpu-nntc# docker run -v $PWD/:/workspace -it sophgo/tpuc_dev:latest初始化环境 root2bb02a2e27d5:/workspace/tpu-nntc# s…

ROS基本框架2——在ROS开发中创建并使用自定义消息(C++版本)

ROS基本框架2——在ROS开发中创建并使用自定义消息(C++版本) code review! 参考笔记 1.ROS基本框架1——编写简单的发布者和订阅者(C++和Python版本) 2.ROS基本框架2——在ROS开发中创建并使用自定义消息(C++版本) 文章目录 ROS基本框架2——在ROS开发中创建并使用自定义…

实例讲解MATLAB绘图坐标轴标签旋转

在进行绘图时需要在图片上添加上做标轴的标签&#xff0c;但是当数据量比较多时&#xff0c;例如一天24小时的数据&#xff0c;这时把每个小时显示在左边轴的标签上&#xff0c;文字内容放不下&#xff0c;因此需要将坐标轴标签旋转一定的角度&#xff0c;这样可以更好在图形上…

Spark 内存管理机制

Spark 内存管理 堆内内存和堆外内存 作为一个 JVM 进程&#xff0c;Executor 的内存管理建立在 JVM(最小为六十四分之一&#xff0c;最大为四分之一)的内存管理之上&#xff0c;此外spark还引入了堆外内存&#xff08;不在JVM中的内存&#xff09;&#xff0c;在spark中是指不…

为什么爱用低秩矩阵

目录 为什么爱用低秩矩阵 一、定义与性质 二、区别与例子 为什么爱用低秩矩阵 我们更多地提及低秩分解而非满秩分解,主要是因为低秩分解在数据压缩、噪声去除、模型简化和特征提取等方面具有显著的优势。而满秩分解虽然能够保持数据的完整性,但在实际应用中的场景较为有限…

Dify+Docker

1. 获取代码 直接下载 &#xff08;1&#xff09;访问 langgenius/dify: Dify is an open-source LLM app development platform. Difys intuitive interface combines AI workflow, RAG pipeline, agent capabilities, model management, observability features and more, …

Android Studio的AI工具插件使用介绍

Android Studio的AI工具插件使用介绍 一、前言 Android Studio 的 AI 工具插件具有诸多重要作用&#xff0c;以下是一些常见的方面&#xff1a; 代码生成与自动补全 代码优化与重构 代码解读 学习与知识获取 智能搜索与资源推荐实际使用中可以添加注释&#xff0c;解读某段代…

DOCKER学习总结

这里写目录标题 一、Docker安装1.1 在线安装1.2 离线安装安装配置启动服务 1.3 配置镜像1.4 Docker启动相关命令 二、Docker三大核心概念2.1 镜像2.2 容器2.3 仓库2.3.1 公有仓库2.3.2 私有仓库 二、容器与虚拟机比较 一、Docker安装 1.1 在线安装 查看是否安装dockeryum lis…

深入浅出体验AI生图产品Dall-E

DALL-E是由OpenAI开发的一种革命性的AI图像生成工具&#xff0c;能够根据文本描述生成图像。它的名字灵感来源于著名画家萨尔瓦多达利&#xff08;Salvador Dal&#xff09;和皮克斯动画电影中的角色瓦力&#xff08;WALL-E&#xff09;&#xff0c;这暗示了其在艺术创造力与技…

OpenCV_Code_LOG

孔洞填充 void fillHole(const Mat srcBw, Mat &dstBw) {Size m_Size srcBw.size();Mat TempMat::zeros(m_Size.height2,m_Size.width2,srcBw.type());//延展图像srcBw.copyTo(Temp(Range(1, m_Size.height 1), Range(1, m_Size.width 1)));cv::floodFill(Temp, Point(…