C++异常详解

文章目录

  • 前言
  • 一、回顾C语言
  • 二、异常的概念
  • 三、异常的使用
    • 1.异常的抛出和捕获
    • 2.异常的重新捕获
  • 三.异常安全与异常规范
    • 1.异常安全
    • 2.异常规范
  • 四.自定义异常体系
  • 五.C++标准库的异常体系
  • 六.异常优缺点
  • 练习题
  • 总结


前言

在本篇文章中,我们将会详细介绍一下有关C++异常的讲解,主要涉及异常的使用,应用场景,异常的优缺点等方面,同时有了异常才会引出我们之后学习的一个非常重要的知识点————智能指针。

一、回顾C语言

c语言解决错误时一般是通过返回错误码的方式,如果遇到非常严重的错误,就会终止程序。

🌟 终止程序:比如我们遇到除零错误,内存错误,空指针的解引用等操作,我们的程序就会终止,程序终止也意味着进程的终止,这有可能会导致大量重要数据的丢失,这是非常严重的。
🌟 返回错误码:也可以通过返回错误码的方式告诉我们相应的错误,但这只是错误码,我们还需要根据错误码查找相关的错误信息,再进一步的分析程序,才能得出具体的错误信息,这时很不便利的。很多库函数都是把错误信息放到error中,表示错误。

二、异常的概念

我们需要程序告诉是是什么错误。

我们C++在解决错误时采用的方法时异常

当函数发现了自己无法处理的错误时抛异常,让函数直接或者间接的调用者处理这个异常。

我们将会引入三个关键字

🌟 throw:出错时,用于抛异常(可在任意位置抛出)
🌟 try:里面放可能抛异常的代码,其中这里的代码称为保护代码,后面跟着catch
🌟 catch:在想要处理的地方,通过异常处理程序捕获异常,可以有多个catch进行捕获

throw…………try
{
}
catch(………)
{
}
catch(……)
{
}

其实异常就存在在我们的日常生活中,就比方说微信在网络不好的时候,会出现一个感叹号告诉你消息发不出去,此时的程序就是在抛异常,告诉你当前网络状态不佳。

如果这个异常按照C语言对错误的处理方式进行操作,整个微信进程就会直接崩溃,强行退出。而采用C++的抛异常机制,将抛出的异常捕获,然后处理,比如当出现网络不好抛异常时,微信就会采取尝试多次发送这样的操作,整个微信程序也不会退出。

正式由于在实际中,很多情况下我们是不希望只要产生异常就直接终止的整个进程的,通过抛异常和捕获处理异常的手段便可让程序保持运行。

三、异常的使用

1.异常的抛出和捕获

异常的抛出和匹配原则

🌟异常通过抛出对象引发,这个对象与catch中()中类型进行匹配,这个对象可以是内置类型,也可以是自定义类型。


#include <iostream>
using namespace std;
double  Division(int x, int y)
{//除零错误,抛异常if (y == 0){throw "Division by zero condition!";}else{return ((double)x / (double)y);}
}void fun()
{int a;int b;cin >> a >> b;cout << Division(a, b) << endl;
}
int main()
{try{fun();}catch (const char*errmsy){cout << errmsy << endl;}return 0;
}

我们运行运行抛异常看一下
在这里插入图片描述
我们确实捕捉到了,打印的就是throw抛的内容。

🌟 我们再匹配相应的catch代码时,如果有多个都满足,选取与throw类型匹配且距离较近的catch.

调用链是指函数栈帧建立的先后顺序,就比如下面代码中main函数优先建立栈帧,然后func函数建立栈帧,最后division函数建立栈帧,这样的顺序就叫调用链。

举个例子说明一下

#include <iostream>
using namespace std;
double  Division()
{int a;int b;cin >> a >> b;if (b == 0){throw "Division by zero condition!";}else{return ((double)a / (double)b);}
}void fun()
{try{Division();}catch (const char* errmsy){cout <<"void fun()" << errmsy << endl;}
}
int main()
{try{fun();}catch (const char* errmsy){cout <<"int main()" << errmsy << endl;}return 0;
}

main函数调用了fun函数,fun函数调用了Division函数。
main函数中catch与fun函数中的catch都与Division中的throw类型匹配,那么他会调用哪个呢??

我们抛异常来看一下
在这里插入图片描述
我们发现调用的是fun中的catch,因为两个都满足。所以找最近的那一个,就是fun函数了。
🌟抛出异常对象后,会生成一个异常对象的拷贝,并不是直接传递给catch()里面的对象,而是将throw对象的拷贝传给catch()中的对象,这个拷贝的临时对象会被catch后销毁。


#include <iostream>
#include <string>
using namespace std;
double  Division(int x, int y)
{//除零错误,抛异常if (y == 0){string s("Division by zero condition!");throw s;}else{return ((double)x / (double)y);}
}void fun()
{int a;int b;cin >> a >> b;cout << Division(a, b) << endl;
}
int main()
{try{fun();}catch (const string ret){cout << ret << endl;}return 0;
}

我们抛出的是一个string的临时对象s,出了作用域销毁了,如果是直接传给外边,传递不过去。
如果我们抛异常了,打印的还是Division by zero condition!,就能够说明生成了拷贝。

我们测试一次看看
在这里插入图片描述
我们发现确实存在拷贝,这和函数参数的传递有异曲同工之妙。

由于临时对象具有常性,所以当抛出的对象是指针时一定注意在形参上加上const才能被接收。这也解释了上面的代码中为什么error_message的类型为什么是const char而不是char
🌟我们一般在catch最后边加上一个catch(…),表示这个catch可以匹配任意类型,但是不知道异常错误是什么。
我们这个东西可以解决很大的问题,如果我们的异常没有对应的catch,就会报错,如果放到程序中,就会崩溃。

#include <iostream>
#include <string>
using namespace std;
double  Division(int x, int y)
{//除零错误,抛异常if (y == 0){throw "Division by zero condition!";}else if(y==1){return ((double)x / (double)y);}else{throw 1;}
}void fun()
{int a;int b;cin >> a >> b;cout << Division(a, b) << endl;
}
int main()
{try{fun();}catch (const string ret){cout << ret << endl;}catch (...){cout << "Unkown error" << endl;}return 0;
}

我们如果输入的第二个数为10,就会抛异常,类型为整形,但是外边没有对应的类型匹配,就会匹配到(…)中。
在这里插入图片描述
🌟有一种特殊情况,不用类型匹配:可以抛出子类对象,使用父类对象再catch中进行捕获,这个在实际中是非常有用的。

在函数调用链中异常栈展开匹配原则

🌟1.查看throw是否在try中,如果在,就进行异常捕获。
🌟2.如果存在匹配的catch,就调到对应的catch进行处理。
🌟3.如果这一层没有匹配的catch,退出当前栈,就到上一层中进行寻找。
🌟4.如果再main函数中,也不存在匹配的catch就报错,
沿着调用链查找匹配catch子句的过程称为栈展开。
🌟5.异常捕获与exit不同。exit直接退出程序,catch处理完异常之后,catch后面的代码会正常执行。

#include <iostream>
#include <string>
using namespace std;
double  Division(int x, int y)
{//除零错误,抛异常if (y == 0){throw "Division by zero condition!";}else if (y == 1){return ((double)x / (double)y);}
}void fun()
{int a;int b;cin >> a >> b;cout << Division(a, b) << endl;
}
int main()
{try{fun();}catch (const string ret){cout << ret << endl;}catch (...){cout << "Unkown error" << endl;}cout << "异常处理后继续执行相关代码" << endl;return 0;
}

在这里插入图片描述

2.异常的重新捕获

有可能单个的catch不能完全处理一个异常,在进行一些校正处理以后,希望再交给更外层的调用
链函数来处理,catch则可以通过重新抛出将异常传递给更上层的函数进行处理。

我们捕获这个异常并不是为了处理这个异常,而是为了干一些其他的事情之后,再把这个异常抛给上层,继续处理。

我们举一个例子理解一下


#include <iostream>
#include <string>
using namespace std;
double  Division(int x, int y)
{//除零错误,抛异常if (y == 0){throw "Division by zero condition!";}else{return ((double)x / (double)y);}
}void fun()
{int*p = new int[10];int a;int b;cin >> a >> b;cout << Division(a, b) << endl;cout << "delete[] p" << endl;delete[] p;
}
int main()
{try{fun();}catch (const string ret){cout << ret << endl;}catch (...){cout << "Unkown error" << endl;}return 0;
}

在fun函数中new了一块空间,现在抛异常看一下


我们new的空间并没有被释放,发生了内存泄漏

这时就需要用到异常的重新捕获了,我们需要在fun函数中对这个异常进行一次捕获,释放new的空间,再抛给main函数。
为我们也可以直接在fun中捕获异常,不在main中处理呀??
而在我们对异常进行一部分操作时,我们更愿意让所有异常在main函数中进行统一处理,比如对异常进行记录日志这样的操作,此时需要重新使用throw抛出异常。

我们看一下解决代码

#include <iostream>
#include <string>
using namespace std;
double  Division(int x, int y)
{//除零错误,抛异常if (y == 0){throw "Division by zero condition!";}else{return ((double)x / (double)y);}
}void fun()
{int*p = new int[10];int a;int b;cin >> a >> b;try{cout << Division(a, b) << endl;}catch (...){cout << "delete[] p" << endl;delete[] p;throw;//捕到什么抛什么}cout << "delete[] p" << endl;delete[] p;
}
int main()
{try{fun();}catch (const string ret){cout << ret << endl;}catch (...){cout << "Unkown error" << endl;}return 0;
}

运行看一下
在这里插入图片描述

一旦catch捕获异常,不能将异常用throw语句再次抛出,这句话是不对的。

三.异常安全与异常规范

1.异常安全

🌟构造函数时用来构造对象和初始化的,最好不要在构造函数抛异常,可能会导致对象不完整或者没有完全初始化。

🌟析构函数是用来释放空间的,对资源进行清理。最好不要在析构函数抛异常,可能会导致资源泄露(内存泄漏,句柄未关闭)。
🌟C++中异常经常会导致资源泄露问题,比如在new和delete中抛异常,导致内存泄漏,在lock和unlock之间抛出异常1导致死锁等问题,我们将会用智能指针来解决这个问题。

2.异常规范

我们在书写异常时,可以抛出任意类型的对象, 异常规格说明的目的是为了让函数使用者知道该函数可能抛出的异常有哪些。

C++98,建议,并不会强制报错,如果我们不这样做了只会有警告,这个是为了兼容c语言做出的让步。

🌟我们可以在函数头后买你加上throw( ),括号里存放可能抛出的异常类型。
throw(char,int char*)就表明了这个函数可能抛出char,int,char这三种类型的异常。
🌟这里表示这个函数只会抛出bad_alloc的异常
void
operator new (std::size_t size) throw (std::bad_alloc);
🌟throw(),表明这个函数只会不会抛出异常
🌟如果无异常接口声明,此函数可以抛任意类型的异常

但是有些异常类型是非常复杂的,为了写出可能发生的异常类型,代价会很大,而且写时太繁琐写出来也不美观。因此,这个建议性的规范很少有人用,也正因为它只是一个建议,所以不使用或者不按要求使用也不会报错。

#include <iostream>
#include <string>
using namespace std;
double  Division(int x, int y) throw()//表明不会抛异常
{//除零错误,抛异常if (y == 0){throw "Division by zero condition!";}else{return ((double)x / (double)y);}
}void fun()
{int a;int b;cin >> a >> b;cout << Division(a,b) << endl; 
}
int main()
{try{fun();}catch (const string ret){cout << ret << endl;}catch (...){cout << "Unkown error" << endl;}return 0;
}

但是如果我们硬要抛异常呢??
在这里插入图片描述
只会存在警告,不会强制报错。

C++11

🌟noexcept,表明函数不会抛异常
但如果我们还是硬抛异常会怎样呢???

在这里插入图片描述
代码就给我们直接挂掉了。

四.自定义异常体系

在以后写代码的时候会遇到小组合作的形式,每个小组负责不同的模块,每个小组都会抛出异常,但是每个小组抛出的异常类型不同,放在一起在main函数中进行捕捉就会很复杂。
实际中抛出和捕获的匹配原则有一个例外,类型可以不完全匹配,抛出子类对象用父类进行捕捉。每个小组都可以抛出派生类的异常,在mian函数中使用基类统一捕捉。

实际项目中,可以创建一个父类,父类中有一个错误码(int)和错误描述信息(string),在这个类里面创建一个虚函数用于返回内部的string对象方便使用人员打印。

class Exception
{
public:Exception(const string& errmsg, int id):_errmsg(errmsg), _id(id){}virtual string what() const{return _errmsg;}
protected:string _errmsg;int _id;
};

不同的小组抛出的异常都会具有本小组的特点,用继承的方式创建一个类,子类中添加一个成员变量记录当前模块的特殊错误信息,并且该类所对应的what函数就可以通过重写来添加一个标志性内容,我们就可以更加容易的知道哪里出了问题。

class SqlException : public Exception
{
public:SqlException(const string& errmsg, int id, const string& sql):Exception(errmsg, id), _sql(sql){}virtual string what() const{string str = "SqlException:";str += _errmsg;str += "->";str += _sql;return str;}
private:const string _sql;
};
class CacheException : public Exception
{
public:CacheException(const string& errmsg, int id):Exception(errmsg, id){}virtual string what() const{string str = "CacheException:";str += _errmsg;return str;}
};
class HttpServerException : public Exception
{
public:HttpServerException(const string& errmsg, int id, const string& type):Exception(errmsg, id), _type(type){}virtual string what() const{string str = "HttpServerException:";str += _type;str += ":";str += _errmsg;return str;}
private:const string _type;
};

那么有了这些描述异常的父子类之后就可以用下面的代码来测试异常:

void SQLMgr()
{srand(time(0));if (rand() % 7 == 0){throw SqlException("权限不足", 100, "select * from name = '张三'");}//throw "xxxxxx";
}
void CacheMgr()
{srand(time(0));if (rand() % 5 == 0){throw CacheException("权限不足", 100);}else if (rand() % 6 == 0){throw CacheException("数据不存在", 101);}SQLMgr();
}
void HttpServer()
{// ...srand(time(0));if (rand() % 3 == 0){throw HttpServerException("请求资源不存在", 100, "get");}else if (rand() % 4 == 0){throw HttpServerException("权限不足", 101, "post");}CacheMgr();
}
int main()
{while (1){this_thread::sleep_for(chrono::seconds(1));try {HttpServer();}catch (const Exception& e) // 这里捕获父类对象就可以{// 多态cout << e.what() << endl;}catch (...){cout << "Unkown Exception" << endl;}}return 0;
}

main函数中首先调用httpserver函数,生成一个随机数,我们把这个数字看作一种情况,如果这个数字能被3 和4整除那么这种情况就出错了,直接使用throw来抛出异常,如果没有出错的花就调用缓冲区的函数,这个函数里面也是相同道理,在这个函数之后就调用数据库的相关函数遇到一些情况就抛出异常,那么在main函数里面我们就可以统一使用父类类型的catch来统一捕获异常,并使用里面的what函数来打印出从的内容,那么上面的代码运行的结果如下:

五.C++标准库的异常体系

C++ 提供了一系列标准的异常,定义在 中,我们可以在程序中使用这些标准的异常。它们是以父
子类层次结构组织起来的,如下所示:

在这里插入图片描述

在这里插入图片描述
由于C++提供的异常体系对项目开发中的异常帮助十分有限,所以这个标准几乎没人用。’

六.异常优缺点

优点

🌟1.相比于错误码,异常可以清晰准确的展现出错误信息,甚至包含堆栈调用的信息,帮助我们更好的定位bug
🌟2.异常会进行多层跳转,不用层层返回进行判断,直到最外层拿到错误。


int ConnnectSql()
{// 用户名密码错误if (...)return 1;// 权限不足if (...)return 2;
}int ServerStart() {if (int ret = ConnnectSql() < 0)return ret;int fd = socket()if(fd < 0return errno;
}int main()
{if (ServerStart() < 0)...return 0;
}

下面这段伪代码我们可以看到ConnnectSql中出错了,先返回给ServerStart,ServerStart再返回给main函数,main函数再针对问题处理具体的错误。

如果是异常体系,不管是ConnnectSql还是ServerStart及调用函数出错,都不用检查,因为抛出的异常异常会直接跳到main函数中catch捕获的地方,main函数直接处理错误。
🌟2.很多的第三方库都包含异常,比如boost、gtest、gmock等等常用的库,那么我们使用它们也需要使用异常
🌟4.部分函数使用异常更好处理。
T& operator这样的函数,如果pos越界了只能使用异常或者终止程序处理,没办法通过返回值表示错误

缺点:

🌟1.导致执行流乱跳,导致我们追踪调式以及分析程序时,比较困难。
🌟2.异常会有一些性能的开销,。当然在现代硬件速度很快的情况下,这个影响基本忽略不计。
🌟3.C++没有垃圾回收机制,资源需要自己管理,有可能发生内存泄漏,死锁等安全问题。
🌟4.C++标准库的异常体系定义得不好,导致大家各自定义各自的异常体系,非常的混乱。
🌟异常尽量规范使用,否则后果不堪设想,随意抛异常,外层捕获的用户苦不堪言。

练习题

如何捕获异常可以使得代码通过编译? ()

class A

{
public:
  A(){}
};

void foo()
{  throw new A; }

A.catch (A x)
B.catch (A * x)
C.catch (A & x)
D.以上都不是

正确答案是B, 异常是按照类型来捕获的,throw后抛出的是A*类型的异常,因此要按照指针方式进行捕获

总结

以上就是今天要讲的内容,本文仅仅详细介绍了C++异常的内容。希望对大家的学习有所帮助,仅供参考 如有错误请大佬指点我会尽快去改正 欢迎大家来评论~~ 😘 😘 😘

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

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

相关文章

微服务架构:注册中心 Eureka、ZooKeeper、Consul、Nacos的选型对比详解

微服务架构&#xff08;Microservices Architecture&#xff09;是一种基于服务拆分的分布式架构模式&#xff0c;旨在将复杂的单体应用程序拆分为一组更小、更独立的服务单元。这些服务单元可以独立开发、测试、部署&#xff0c;并使用不同的技术栈和编程语言。它们通过轻量级…

[华为OD] C卷 dfs 特殊加密算法 100

题目&#xff1a; 有一种特殊的加密算法&#xff0c;明文为一段数字串&#xff0c;经过密码本查找转换&#xff0c;生成另一段密文数字串。 规则如下 1•明文为一段数字串由0-9组成 2.密码本为数字0-9组成的二维数组 3•需要按明文串的数字顺序在密码本里找到同样的数字串…

PUBG非升级实用枪皮-部分盘点

藏匿处的黑货箱武器需要耗费高额成本才能升级 对于像我这样的日常休闲玩家来说是一笔不小的&#xff08;巨大的&#xff01;&#xff09;负担 其实有许多普通非升级枪皮也是不错的选择 今天就来盘点一下我自己日常在用的普通皮 来看看你是不是也在用一样的 &#xff08;仅是盘点…

【OceanBase诊断调优】—— 租户资源统计项及其查询方法

本文主要介绍 OceanBase 数据库中租户资源统计项及其查询方法。 适用版本 OceanBase 数据库 V4.1.x、V4.2.x 版本。 CPU 资源统计项 逻辑 CPU 使用率&#xff08;线程处理请求的时间占比&#xff09;。 通过虚拟表 __all_virtual_sysstat 在 SYS 系统租户下&#xff0c;查看…

09.zabbix自定义模块并使用

zabbix自定义模块并使用 根据tcp的11中状态获取值&#xff0c;进行批量配置监控项 [rootyunlong66 ~]# cat /etc/zabbix/zabbix_agentd.d/tcp.conf UserParameterESTABLISHED,netstat -antp |grep -c ESTABLISHED UserParameterSYN_SENT,netstat -antp |grep -c SYN_SENT Use…

Obsidian/Typora设置图床

在obsidian中默认图片是保存在本地的&#xff0c;但是在要导出文档上传到网上时&#xff0c;由于图片保存在本地&#xff0c;会出现无法加载图片的问题。 这里引用的一段话&#xff1a; 这里使用picgo-core和gitee实现图床功能&#xff0c; 参考1&#xff1a; Ubuntu下PicGO配…

Github学习

1.Git与Github 区别: Git是一个分布式版本控制系统&#xff0c;简单的说就是一个软件&#xff0c;用于记录一个或若干个文件内容变化&#xff0c;以便将来查阅特点版本修订情况的软件。 Github是一个为用户提高Git服务的网站&#xff0c;简单说就是一个可以放代码的地方。Gi…

C语言 | Leetcode C语言题解之第85题最大矩形

题目&#xff1a; 题解&#xff1a; int maximalRectangle(char** matrix, int matrixSize, int* matrixColSize) {int m matrixSize;if (m 0) {return 0;}int n matrixColSize[0];int left[m][n];memset(left, 0, sizeof(left));for (int i 0; i < m; i) {for (int j …

SeetaFace6人脸活体检测C++代码实现Demo

SeetaFace6包含人脸识别的基本能力&#xff1a;人脸检测、关键点定位、人脸识别&#xff0c;同时增加了活体检测、质量评估、年龄性别估计&#xff0c;并且顺应实际应用需求&#xff0c;开放口罩检测以及口罩佩戴场景下的人脸识别模型。 官网地址&#xff1a;https://github.co…

【补充】图神经网络前传——DeepWalk

论文阅读 论文&#xff1a;https://arxiv.org/pdf/1403.6652 参考&#xff1a;【论文逐句精读】DeepWalk&#xff0c;随机游走实现图向量嵌入&#xff0c;自然语言处理与图的首次融合_随机游走图嵌入-CSDN博客 abstract DeepWalk是干什么的&#xff1a;在一个网络中学习顶点…

【Mac】Ghost Buster Pro(苹果电脑内存清理专家) v3.2.5安装教程

软件介绍 Ghost Buster pro是一款针对Mac系统的电脑清理和优化工具&#xff0c;可以帮助用户清理系统垃圾、修复注册表错误、卸载不需要的软件、管理启动项等&#xff0c;从而提高系统性能和稳定性。 安装教程 1.打开镜像包&#xff0c;拖动「Ghost Buster Pro」到应用程序中…

【Linux网络】Https【下】{CA认证/证书的签发与认证/安全性/总结}

文章目录 1.引入证书【为方案五铺垫】1.1再谈https1.2SSL/TLS1.3CA机构1.4理解数字签名1.4继续铺垫1.5方案五服务端申请证书回顾一二三回顾方案四方案五过程寻找方案五的漏洞客⼾端对证书进⾏认证 2.查看证书2.1查看浏览器的受信任证书发布机构2.2中间⼈有没有可能篡改该证书2.…

差分约束 C++ 算法例题

差分约束 差分约束 是一种特殊的 n 元一次不等式组&#xff0c;m 个约束条件&#xff0c;可以组成形如下的格式&#xff1a; { x 1 − x 1 ′ ≤ y 1 x 2 − x 2 ′ ≤ y 2 ⋯ x m − x m ′ ≤ y m \begin{cases} x_1-x_1^{} \le y_1 \\ x_2-x_2^{} \le y_2 \\ \cdots \\ x_…

SpringBoot集成jxls2实现复杂(多表格)excel导出

核心依赖 需求 导出多个表格&#xff0c;包含图片&#xff0c;类似商品标签 1.配置模板 创建一个xlsx的模板文件&#xff0c;配置如下 该模板进行遍历了两次&#xff0c;因为我想要导出的数据分为两列展示&#xff0c;左右布局&#xff0c;一个循环实现不了&#xff0c;所以采…

百面算法工程师 | 正则优化函数——BN、LN、Dropout

本文给大家带来的百面算法工程师是正则优化函数&#xff0c;文章内总结了常见的提问问题&#xff0c;旨在为广大学子模拟出更贴合实际的面试问答场景。在这篇文章中&#xff0c;我们将总结一些BN、LN、Dropout的相关知识&#xff0c;并提供参考的回答及其理论基础&#xff0c;以…

C++ | Leetcode C++题解之第85题最大矩形

题目&#xff1a; 题解&#xff1a; class Solution { public:int maximalRectangle(vector<vector<char>>& matrix) {int m matrix.size();if (m 0) {return 0;}int n matrix[0].size();vector<vector<int>> left(m, vector<int>(n, 0)…

【HCIP学习】BGP对等体组、聚合、路由反射器、联盟、团体属性

一、大规模BGP网络所遇到的问题 BGP对等体众多&#xff0c;配置繁琐&#xff0c;维护管理难度大 BGP路由表庞大&#xff0c;对设备性能提出挑战 IBGP全连接&#xff0c;应用和管理BGP难度增加&#xff0c;邻居数量过多 路由变化频繁&#xff0c;导致路由更新频繁 二、解决大…

使用QT-QSqlQuery::value()遇到的问题

在实现客户端间好友添加功能时&#xff0c;我通过以下函数想实现数据库对好友信息的保存 bool OpeDB::handleAddFriend_repound(const char *pername, const char *name) { // pername 被添加方 name 申请添加方 qDebug() << pername << " " &l…

Nginx(简洁版)

基本配置 worker_processes 1; //默认为1&#xff0c;表示开启一个业务进程 events //事件驱动模块&#xff08;&#xff09;{worker_connections 1024; //单个业务进程可接受连接数 } http{include mime.types; // 引入http mime类型&#xff08;在外部已经定义好&#xff0c…

如何在huggingface上申请下载使用llama2/3模型

1. 在对应模型的huggingface页面上提交申请 搜索对应的模型型号 登录huggingface&#xff0c;在模型详情页面上&#xff0c;找到这个表单&#xff0c;填写内容&#xff0c;提交申请。需要使用梯子&#xff0c;country填写梯子的位置吧(比如美国&#xff09; 等待一小时左右…