异常知识及其使用
异常的简单概念
在C++中,异常处理是一种机制,用于处理程序运行时发生的意外情况。它允许程序在发生错误时,将控制权转移到一个专门的代码块,而不是让程序直接崩溃。C++的异常处理机制包括以下几个关键概念:
throw
- 用途:用于抛出一个异常。
- 语法:
throw exception;
- 说明:
throw
关键字后面可以跟任何类型的表达式,包括基本数据类型、对象、异常类实例等。当throw
被执行时,程序控制流将立即转移到最近的匹配的catch
块。 - 示例:
throw std::runtime_error("An error occurred");
try
- 用途:标记可能抛出异常的代码块。
- 语法:
try { /* 可能抛出异常的代码 */ }
- 说明:
try
块包含了可能引发异常的代码。如果try
块中的代码抛出了异常,程序将搜索匹配的catch
块来处理这个异常。如果没有匹配的catch
块,异常将被传播到调用栈的上一层。 - 示例:
try {// 代码可能抛出异常 } catch (const std::exception& e) {// 处理std::exception类型的异常 }
catch
- 用途:捕获并处理异常。
- 语法:
catch (exception_type exception_name) { /* 处理异常的代码 */ }
- 说明:
catch
块用于捕获特定类型的异常,并提供处理异常的代码。一个try
块后面可以有多个catch
块,以捕获不同类型的异常。 - 示例:
try {// 可能抛出多种类型的异常 } catch (const std::bad_alloc& e) {// 处理内存分配失败的异常 } catch (const std::exception& e) {// 处理其他标准异常 } catch (...) {// 处理所有未被捕获的异常 }
组合使用:
try
、catch
和throw
通常一起使用,以实现异常的抛出和捕获。throw
用于在检测到错误时立即将错误信息传递出去,try
块用于包围可能抛出异常的代码,而catch
块则用于处理这些异常。- 异常处理允许程序的某一部分专注于检测问题,而另一部分专注于解决问题,从而实现代码的解耦和模块化。
异常的抛出和捕获
异常抛出(throw)
-
引发异常:当程序遇到一个无法处理的情况时,可以通过
throw
关键字抛出一个异常对象。这个对象可以是任何类型,包括自定义的异常类实例、标准异常类实例或者基本数据类型。
double Division(int a, int b)
{//除数为0无意义,需要抛异常if (b == 0){throw (string)"Division by zero condition!";}//这里不需要else,是因为如果有抛异常,一旦throw被执行,throw之后的代码将不会被执行。程序的执行流程会立即跳转到匹配的catch块。return ((double)a) / ((double)b);
}
这里我们本来抛出的异常对象类型是 const char* ,这里我通过强制类型转换,将该抛异常的对象的类型转换成 string 。
-
调用链和匹配:抛出的异常对象的类型将决定哪个
catch
块会捕获这个异常。编译器会在调用链中寻找匹配的catch
块,即类型匹配且最接近抛出点的catch
块。

-
执行流程的改变:一旦
throw
被执行,throw
之后的代码将不会被执行。程序的执行流程会立即跳转到匹配的catch
块。如果当前函数中没有匹配的catch
块,异常会沿着调用栈向上传播,直到找到匹配的catch
块或者程序终止。
- 沿着调⽤链的函数可能提早退出。
- ⼀旦程序开始执⾏异常处理程序,沿着调⽤链创建的对象都将销毁。(不能匹配的栈帧就被直接销毁了,它后面的代码不会执行)
根据上图我们可以看出来:Division()进行抛异常后,除法运算没有被执行,而又因为Division()中的catch不匹配,没有进行catch的能力,所以 cout << "double Division(int a, int b)" << endl; 没有被执行。(控制权从throw位置转移到了catch位置)
我们可以发现:
if (b == 0)
{int* p = new(int);throw (string)"Division by zero condition!";
}
这样写的话,如果在该生命周期没有因为抛异常被catch,造成没有对该new的资源的释放,就会引起内存泄露等问题,解决该问题,我们主要是通过智能指针进行解决。
异常捕获(catch)
-
处理异常:
catch
块用于捕获并处理异常。它可以捕获特定类型的异常,也可以捕获所有类型的异常(使用catch(...)
)。
目的是为了不让程序直接挂掉,也是为了防止当catch和抛出异常不匹配的,导致执行流乱跳,但是问题就是不知道异常错误是什么。
-
对象销毁:当控制权转移到
catch
块时,从抛出异常点到catch
块之间的所有局部对象都会被销毁。这是因为C++的异常处理机制要求在异常传播过程中,所有局部对象都必须被正确地析构。 -
异常对象的拷贝:当抛出一个异常时,如果抛出的是一个非静态局部对象(即在函数内部定义的对象),编译器会创建这个对象的一个拷贝。这个拷贝会被传递给
catch
块,并且在catch
块执行完毕后被销毁。这样做的目的是为了确保异常对象在传递过程中的安全性和完整性。
即使std::string
具有移动构造函数,当抛出一个std::string
对象时,如果没有移动构造函数,编译器仍然会创建一个拷贝。这是因为异常对象需要被传递给catch
块,并且必须在整个传递过程中保持有效。
由于移动构造的存在,所以没有进行临时拷贝,如果没有移动构造,抛出的对象是会被拷贝一份的。(C++11引入了移动语义,允许资源的转移而不是拷贝。如果一个对象具有移动构造函数,那么在右值情况下,可以调用移动构造函数来转移资源,而不是拷贝资源。)
栈展开
- 抛出异常后,程序暂停当前函数的执⾏,开始寻找与之匹配的catch⼦句,⾸先检查throw本⾝是否在try块内部,如果在则查找匹配的catch语句,如果有匹配的,则跳到catch的地⽅进⾏处理。
- 如果当前函数中没有try/catch⼦句,或者有try/catch⼦句但是类型不匹配,则退出当前函数,继续在外层调⽤函数链中查找,上述查找的catch过程被称为栈展开。
- 如果到达main函数,依旧没有找到匹配的catch⼦句,程序会调⽤标准库的 terminate 函数终⽌程序。(这里我们可以用catch(...)来进行间接阻断程序的直接报错)
- 如果找到匹配的catch⼦句处理后,catch⼦句代码会继续执⾏。
其实我们上面的图一示例就是一个栈展开的过程

查找匹配的处理代码
- ⼀般情况下抛出对象和catch是类型完全匹配的,如果有多个类型匹配的,就选择离他位置更近的那个。(这在上面图一也有详细说明)
- 但是也有⼀些例外,允许从⾮常量向常量的类型转换,也就是权限缩⼩;允许数组转换成指向数组元素类型的指针,函数被转换成指向函数的指针;允许从派⽣类向基类类型的转换,这个点⾮常实⽤,实际中继承体系基本都是⽤这个⽅式设计的。
#include <iostream>
#include <string>class Base {};
class Derived : public Base {};void handleBase(const Base& b) {std::cout << "Caught const Base by reference" << std::endl;
}void handleBasePtr(Base* b) {std::cout << "Caught Base*" << std::endl;
}void handleDerived(Derived d) {std::cout << "Caught Derived by value" << std::endl;
}int main() {Derived d;const Base& b = d;try {// 抛出Derived类型的对象throw d;} catch (const Base& e) { // 匹配规则:const转换handleBase(e);} catch (Base* e) { // 匹配规则:数组到指针的转换(这里Base是多态类型,所以适用)handleBasePtr(e);}try {// 抛出Derived类型的对象throw d;} catch (Derived e) { // 精确匹配handleDerived(e);}return 0;
}
- 如果到main函数,异常仍旧没有被匹配就会终⽌程序,不是发⽣严重错误的情况下,我们是不期望程序终⽌的,所以⼀般main函数中最后都会使⽤catch(...),它可以捕获任意类型的异常,但是是不知道异常错误是什么。
一般大型项目程序才会使用异常,下面我们模拟设计一个服务的几个模块,每个模块的继承都是Exception的派生类,每个模块可以添加自己的数据,最后捕获时,我们捕获基类就可以:
#include<thread>//Exception:基类,包含错误信息和ID。
class Exception
{
public:Exception(const string& errmsg, int id):_errmsg(errmsg), _id(id){}virtual string what() const{return _errmsg;}int getid() const{return _id;}
protected:string _errmsg;int _id;
};//SqlException:数据库相关的异常,包含SQL语句。
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;
};//CacheException:缓存相关的异常。
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;}
};//HttpException:HTTP服务相关的异常,包含请求类型。
class HttpException : public Exception
{
public:HttpException(const string& errmsg, int id, const string& type):Exception(errmsg, id), _type(type){}virtual string what() const{string str = "HttpException:";str += _type;str += ":";str += _errmsg;return str;}private:const string _type;
};//SQLMgr:模拟数据库管理,随机抛出SqlException。
void SQLMgr()
{if (rand() % 7 == 0){throw SqlException("权限不足", 100, "select * from name = '张三'");}else{cout << "SQLMgr 调用成功" << endl;}
}//CacheMgr:模拟缓存管理,随机抛出CacheException,并调用SQLMgr。
void CacheMgr()
{if (rand() % 5 == 0){throw CacheException("权限不足", 100);}else if (rand() % 6 == 0){throw CacheException("数据不存在", 101);}else{cout << "CacheMgr 调用成功" << endl;}SQLMgr();
}//HttpServer:模拟HTTP服务,随机抛出HttpException,并调用CacheMgr。
void HttpServer()
{if (rand() % 3 == 0){throw HttpException("请求资源不存在", 100, "get");}else if (rand() % 4 == 0){throw HttpException("权限不足", 101, "post");}else{cout << "HttpServer调用成功" << endl;}CacheMgr();
}int main()
{srand(time(0));while (1){this_thread::sleep_for(chrono::seconds(1));try{HttpServer();}//利用继承中的赋值兼容转换,切片操作catch (const Exception& e) // 这里捕获基类,基类对象和派生类对象都可以捕获{// 多态调用// 如果没有将what()声明为虚函数,那么使用基类指针或引用调用what()时// 将始终调用基类的实现,这将不会反映出派生类的特定错误信息,从而失去多态的优势cout << e.what() << endl;}catch (...){cout << "Unkown Exception" << endl;}}return 0;
}
这种像日志一样记录,我们就可以清楚的知道了时哪一个模块的错误,这样就可以更高效的找到抛异常的地方,更快的解决问题 。
异常重新抛出
有时catch到⼀个异常对象后,需要对错误进⾏分类,其中的某种异常错误需要进⾏特殊的处理,其他错误则重新抛出异常给外层调⽤链处理。捕获异常后需要重新抛出,直接 throw ; 就可以把捕获的对象直接抛出。
// 下面程序模拟展示了聊天时发送消息,发送失败补货异常,但是可能在
// 电梯地下室等场景手机信号不好,则需要多次尝试,如果多次尝试都发
// 送不出去,则就需要捕获异常再重新抛出,其次如果不是网络差导致的
// 错误,捕获后也要重新抛出。
void _SendMsg(const string& s)
{if (rand() % 2 == 0){throw HttpException("网络不稳定,发送失败", 102, "put");}else if (rand() % 7 == 0){throw HttpException("你已经不是对象的好友,发送失败", 103, "put");}else{cout << "发送成功" << endl;}
}void SendMsg(const string& s)
{// 发送消息失败,则再重试3次for (size_t i = 0; i < 4; i++){try{_SendMsg(s);break;}catch (const Exception& e){// 捕获异常,if中是102号错误,网络不稳定,则重新发送// 捕获异常,else中不是102号错误,则将异常重新抛出if (e.getid() == 102){// 重试三次以后否失败了,则说明网络太差了,重新抛出异常if (i == 3)throw;cout << "开始第" << i + 1 << "重试" << endl;}else{// 重新抛出throw;}}}
}int main()
{srand(time(0));string str;while (cin >> str){try{SendMsg(str);}catch (const Exception& e){cout << e.what() << endl << endl;}catch (...){cout << "Unkown Exception" << endl;}}return 0;
}
异常安全问题
- 异常抛出后,后⾯的代码就不再执⾏,前⾯申请了资源(内存、锁等),后⾯进⾏释放,但是中间可能会抛异常就会导致资源没有释放,这⾥由于异常就引发了资源泄漏,产⽣安全性的问题。中间我们需要捕获异常,释放资源后⾯再重新抛出,当然后⾯智能指针章节讲的RAII⽅式解决这种问题是更好的。
- 其次析构函数中,如果抛出异常也要谨慎处理,⽐如析构函数要释放10个资源,释放到第5个时抛出异常,则也需要捕获处理,否则后⾯的5个资源就没释放,也资源泄漏了。《Effctive C++》第8个条款也专⻔讲了这个问题,别让异常逃离析构函数。
#include <exception>double Divide(int a, int b) {if (b == 0) {throw "Division by zero condition!";}return (double)a / (double)b;
}void Func() {//std::vector<int> array(10); // 使用vector自动管理内存int* array = new int[10];try {int len, time;cin >> len >> time;cout << Divide(len, time) << std::endl;}catch (...) {// 捕获异常释放内存cout << "delete []" << array << endl;delete[] array;throw; // 异常重新抛出,捕获到什么抛出什么}cout << "delete []" << array << endl;delete[] array;
}int main() {try {Func();}catch (const char* errmsg) {std::cout << errmsg << std::endl;}catch (const std::exception& e) {std::cout << e.what() << std::endl;}catch (...) {std::cout << "Unknown Exception" << std::endl;}return 0;
}
对于Func函数中:在catch (...)
块中,捕获所有类型的异常,释放动态分配的内存(catch(...)就是为了这个),并重新抛出异常,以便外层catch
块可以进一步处理。
异常规范
-
构造函数和析构函数中的异常:
- 避免在构造函数中抛出异常:如果在构造函数中抛出异常,可能会导致对象处于不完整或未完全初始化的状态,这可能会导致资源泄漏或程序崩溃。
- 避免在析构函数中抛出异常:如果在析构函数中抛出异常,可能会导致资源释放不完全,从而引发资源泄漏。
-
异常安全:
- 在C++中,异常处理需要特别注意资源管理,以避免因异常而导致的资源泄漏问题。例如,在
new
和delete
操作中,如果抛出异常,可能会导致内存泄漏。
- 在C++中,异常处理需要特别注意资源管理,以避免因异常而导致的资源泄漏问题。例如,在
-
异常规范:
- C++委员会提出了一套建议性规范,用于明确函数可能抛出的异常类型。这包括在函数声明后使用
throw(类型)
来列出可能抛出的异常类型,或使用throw()
表示函数不抛出异常。 - 然而,这种规范较为繁琐,且在实际应用中较少使用,因为它只是一个建议,不遵循也不会导致编译错误。
- C++委员会提出了一套建议性规范,用于明确函数可能抛出的异常类型。这包括在函数声明后使用
-
C++11的改进:
- C++11引入了
noexcept
关键字,用于简化异常声明。在不会抛出异常的函数后添加noexcept
,可以明确表示该函数不会抛出异常,从而使异常声明更加简洁。
- C++11引入了
以下是一些示例代码,展示了如何在C++中应用这些最佳实践:
#include <iostream>
#include <new> // 用于std::bad_allocclass Resource {
public:Resource() {// 构造函数中避免抛出异常}~Resource() noexcept {// 析构函数中避免抛出异常// noexcept确保析构函数不会抛出异常}
};void func() noexcept {// 表示这个函数不会抛出异常
}void mayThrow() {throw std::bad_alloc(); // 可能抛出异常
}int main() {Resource r;try {mayThrow();} catch (const std::bad_alloc& e) {std::cout << "Caught bad_alloc: " << e.what() << std::endl;}return 0;
}
在这个示例中:
Resource
类的构造函数和析构函数都被设计为不会抛出异常,以避免资源泄漏和对象状态不一致的问题。func
函数使用noexcept
关键字声明,表示它不会抛出异常。mayThrow
函数可能抛出std::bad_alloc
异常,这需要在调用它的代码中进行处理。
通过遵循这些最佳实践,可以提高C++程序的健壮性和异常安全性。
int i = 0;// 检查表达式是否是noexcept
cout << noexcept(Divide(1, 2)) << endl;
cout << noexcept(Divide(1, 0)) << endl;
cout << noexcept(++i) << endl;
结果:
(0,0,1)
C语言和C++的错误处理区别
1. 错误处理方式
C语言:
- 错误码:C语言通常使用返回值或全局变量来传递错误信息。函数执行完毕后,会返回一个特定的错误码,表示操作是否成功或失败。调用者需要检查这个返回值,并根据错误码查询相应的错误信息。
- 缺点:这种方式需要调用者记住各种错误码的含义,增加了记忆负担,且代码可读性较差。
C++异常处理:
- 异常对象:C++通过抛出异常对象来处理错误。异常对象可以携带丰富的错误信息,包括错误类型、错误消息等。
- 优点:异常处理使得代码更加简洁和易于理解,因为异常对象可以包含详细的错误信息,调用者不需要记住错误码,只需要捕获异常并处理即可。
2. 代码结构和可读性
C语言:
- 错误检查:在C语言中,函数调用后通常需要检查返回值,这可能导致代码中出现大量的
if
语句,降低了代码的可读性。
C++异常处理:
- 分离错误检测和处理:在C++中,错误检测和错误处理可以完全分离。检测到错误的地方只需抛出异常,而处理异常的地方则在
catch
块中定义,这样使得代码更加模块化,提高了代码的可读性和维护性。
3. 异常的传递和处理
C语言:
- 函数调用链:在C语言中,如果一个函数发生错误,它需要返回错误码,调用者需要处理这个错误码,然后可能需要再次调用其他函数来处理错误,这形成了一个错误处理的调用链。
C++异常处理:
- 异常传播:在C++中,如果一个函数抛出异常而没有被捕获,异常会沿着调用栈向上传播,直到被一个
catch
块捕获。这种方式简化了错误处理流程,因为不需要在每个函数调用后都检查错误码。
4. 异常对象的灵活性
C++异常处理:
- 自定义异常类:C++允许开发者定义自己的异常类,这些类可以继承自
std::exception
。这样,异常对象不仅可以包含错误消息,还可以包含其他有用的信息,如错误发生的位置、时间等。
5. 性能考虑
C语言:
- 性能开销:使用错误码的方式通常没有额外的性能开销,因为不需要额外的内存分配和对象复制。
C++异常处理:
- 性能开销:异常处理可能会带来一定的性能开销,因为需要分配内存来创建异常对象,以及可能的栈展开(stack unwinding)操作。因此,在性能敏感的场合,需要谨慎使用异常。
总结来说,C++的异常处理机制提供了一种更加灵活和强大的错误处理方式,它允许开发者将错误检测和处理逻辑分离,提高了代码的可读性和维护性。然而,这种机制也带来了一定的性能开销,因此在设计程序时需要权衡使用。
异常处理的注意事项
-
提早退出:如果一个函数在执行过程中抛出了异常,并且这个异常没有在该函数内部被捕获,那么这个函数将提早退出,并且不会执行任何清理代码。因此,使用异常处理时,需要确保资源的正确管理和释放。
-
栈展开(Stack Unwinding):当异常沿着调用栈传播时,会触发栈展开过程,这个过程会销毁抛出点和捕获点之间的所有局部对象,并调用它们的析构函数。
-
性能考虑:异常处理可能会带来性能开销,因为涉及到栈展开和异常对象的拷贝。因此,在性能敏感的代码中,应该尽量避免使用异常处理。
-
异常安全:在设计程序时,应该考虑到异常安全,确保在异常发生时,程序的状态仍然保持一致,资源得到正确释放。
相关文章:
异常知识及其使用
异常的简单概念 在C中,异常处理是一种机制,用于处理程序运行时发生的意外情况。它允许程序在发生错误时,将控制权转移到一个专门的代码块,而不是让程序直接崩溃。C的异常处理机制包括以下几个关键概念: throw 用途&…...
Spark常问面试题---项目总结
一、数据清洗,你都清洗什么?或者说 ETL 你是怎么做的? 我在这个项目主要清洗的式日志数据,日志数据传过来的json格式 去除掉无用的字段,过滤掉json格式不正确的脏数据 过滤清洗掉日志中缺少关键字段的数据ÿ…...
哈希及其模拟实现
1.哈希的概念 顺序结构以及平衡树中,元素的关键码与其存储位置之间没有对应的关系。因此,在查找一个元素时,必须要经过关键码的多次比较。顺序查找的时间复杂度为O(N),平衡树中为树的高度,即O(log_2 N),搜…...
Day 32 动态规划part01
今天正式开始动态规划! 理论基础 无论大家之前对动态规划学到什么程度,一定要先看 我讲的 动态规划理论基础。 如果没做过动态规划的题目,看我讲的理论基础,会有感觉 是不是简单题想复杂了? 其实并没有,我讲的理论基础内容,在动规章节所有题目都有运用,所以很重要!…...
【娱乐项目】竖式算术器
Demo介绍 一个加减法随机数生成器,它能够生成随机的加减法题目,并且支持用户输入答案。系统会根据用户输入的答案判断是否正确,统计正确和错误的次数,并显示历史记录和错题记录。该工具适合用于数学练习,尤其适合练习基…...
XRP 深度解析:从技术到 Meme 币交易指南
撰文:Ignas | DeFi Research 编译:Yuliya,PANews 本文来源Techub News:XRP 深度解析:从技术到 Meme 币交易指南 在当前加密货币市场,一个令人瞩目的现象正在上演:XRP 在短短一个月内暴涨 3.5 倍…...
机器学习周志华学习笔记-第13章<半监督学习>
机器学习周志华学习笔记-第13章<半监督学习> 卷王,请看目录 13半监督学习13.1 生成式方法13.2 半监督SVM13.3 基于分歧的方法13.4 半监督聚类 13半监督学习 前面我们一直围绕的都是监督学习与无监督学习,监督学习指的是训练样本包…...
【MySql】navicat连接报2013错误
navicat连接mysql报2013错误 报错信息1、检验Mysql数据库是否安装成功2、对Mysql的配置文件进行修改配置2.1、找到配置文件2.2、Linux下修改配置文本 3、连接进入mysql服务4、在mysql下执行授权命令 报错信息 Navicat连接mysql报2013错误 2013-Lost connection to MYSQL serve…...
【微服务】Docker
一、Docker基础 1、依赖的兼容问题:Docker允许开发中将应用、依赖、函数库、配置一起打包,形成可移植镜像Docker应用运行在容器中,使用沙箱机制,相互隔离。 2、如何解决开发、测试、生产环境有差异的问题:Docker镜像…...
renderExtraFooter 添加本周,本月,本年
在 Ant Design Vue 中,a-date-picker 组件提供了一个 renderExtraFooter 属性,可以用来渲染额外的页脚内容。你可以利用这个属性来添加“本周”、“本月”和“本年”的按钮。下面是如何在 Vue 2 项目中实现这一功能的具体步骤: 1.确保安装了…...
警惕开源信息成为泄密源头
文章目录 前言一、信息公开需谨慎1、警惕采购招标泄密。2、警惕信息公开泄密。3、警惕社交媒体泄密。 二、泄密风险需严防1、健全制度,明确责任。2、加强管控,严格审查。3、提高意识,谨言慎行。 前言 大数据时代,信息在网络空间发…...
密码学和CA证书
参考视频 一. 公钥私钥的理解 我们提到的使用公钥私钥进行加密解密只是一种口头表达方式,准确来说应该是公钥和私钥通过加密 算法生成,也需要通过配合加密算法进行解密。而不是直接用公钥和私钥进行加密解密。 二. 对称加密和非对称加密算法 1. 非对…...
Python 入门教程(2)搭建环境 | 2.4、VSCode配置Node.js运行环境
文章目录 一、VSCode配置Node.js运行环境1、软件安装2、安装Node.js插件3、配置VSCode4、创建并运行Node.js文件5、调试Node.js代码 一、VSCode配置Node.js运行环境 1、软件安装 安装下面的软件: 安装Node.js:Node.js官网 下载Node.js安装包。建议选择L…...
Nginx Web服务器管理、均衡负载、访问控制与跨域问题
Nginx Web 服务器的均衡负载、访问控制与跨域问题 Nginx 的配置 1. 安装Nginx 首先安装Nginx apt install nginx -ycaccpurgatory-v:~$ sudo apt install nginx [sudo] password for cacc: Reading package lists... Done Building dependency tree... Done Reading state i…...
排序学习整理(2)
上集回顾 排序学习整理(1)-CSDN博客 2.3 交换排序 交换排序的基本思想是:根据序列中两个记录键值的比较结果,交换这两个记录在序列中的位置。 特点: 通过比较和交换操作,将键值较大的记录逐步移动到序列…...
【前端】将vue的方法挂载到window上供全局使用,也方便跟原生js做交互
【前端】将vue的方法挂载到window上供全局使用,也方便跟原生js做交互 <template><div><el-button click"start">调用方法</el-button></div> </template> <script> // import { JScallbackProc } from ./JScal…...
单片机的中断系统
作者简介 彭煜轩,男,银川科技学院计算机与人工智能学院,2022级计算机与科学技术8班本科生,单片机原理及应用课程第3组。 指导老师:王兴泽 电子邮件:1696409709qq.com 前言 本篇文章是参考《单片机原理…...
Java基础面向对象(接口高级)
高版本的接口 JDK8.0 普通的公开非抽象方法(默认方法) [public] default 返回值类型 方法名(形参列表){//操作语句 } default: 在此位置身份为非抽象标识 接口中的非抽象方法实现类不需要进行重写且通常不会进行重写 当父类与接口的方法体出现冲突时, 优先执行父类内容 (类优…...
OpenCV圆形标定板检测算法findCirclesGrid原理详解
OpenCV的findCirclesGrid函数检测圆形标定板的流程如下: findCirclesGrid函数源码: //_image,输入图像 //patternSize,pattern的宽高 //_centers,blobs中心点的位置 //flags,pattern是否对称 //blobDetector,这里使用的是SimpleBlobDetector bool cv::findCirclesGrid(…...
Linux 网卡收包流程如下
Linux 网卡收包流程如下 网卡收到数据包将数据包从网卡硬件缓存移动到服务器内存中(DMA方式,不经过CPU)通过硬中断通知CPU处理CPU通过软中断通知内核处理经过TCP/IP协议栈处理应用程序通过read()从socket buffer读取数据 网卡丢包 我们先看下ifconfig的输出&#…...
普中51单片机——LED流水灯模块
1、GPIO概念 GPIO(general purpose intput output)是通用输入输出端口的简称,可以通过软件来控制其输入和输出。51 单片机芯片的 GPIO 引脚与外部设备连接起来,从而实现与外部通讯、 控制以及数据采集的功能。 1.1、GPIO分类 &a…...
Linux 各个目录作用
刚毕业的时候学习Linux基础知识,发现了一份特别好的文档快乐的 Linux 命令行,翻译者是happypeter,作者当年也在慕课录制了react等前端相关的视频,通俗易懂,十分推荐 关于Linux的目录,多数博客已有详细介绍…...
【包教包会】CocosCreator3.x——重写Sprite,圆角、3D翻转、纹理循环、可合批调色板、不影响子节点的位移旋转缩放透明度
一、效果演示 重写Sprite组件,做了以下优化: 1、新增自变换,在不影响子节点的前提下位移、旋转、缩放、改变透明度 新增可合批调色板,支持色相、明暗调节 新增圆角矩形、3D透视旋转、纹理循环 所有功能均支持合批、原生平台&…...
腾讯阅文集团Java后端开发面试题及参考答案
Java 的基本数据类型有哪些?Byte 的数值范围是多少? Java 的基本数据类型共有 8 种,可分为 4 类: 整数类型:包括 byte、short、int 和 long。byte 占 1 个字节,其数值范围是 - 128 到 127,用于表示较小范围的整数,节省内存空间,在处理一些底层的字节流数据或对内存要求…...
Kafka如何保证消息可靠?
大家好,我是锋哥。今天分享关于【Kafka如何保证消息可靠?】面试题。希望对大家有帮助; Kafka如何保证消息可靠? 1000道 互联网大厂Java工程师 精选面试题-Java资源分享网 Kafka通过多种机制来确保消息的可靠性,主要包…...
【layui】tabs 控件内通过 iframe 加载url 方式渲染tab页面
<!DOCTYPE html> <html><head><meta charset"UTF-8"><title>tabs 内部使用 iframe 嵌套 url 页面</title><link rel"stylesheet" href"../../../libs/layui/layui-2.4.5/dist/css/layui.css"><scr…...
EtherCAT转DeviceNe台达MH2与欧姆龙CJ1W-DRM21通讯案例
一.案例背景 台达MH2设备通常采用EtherCAT通信协议,这种协议在高速实时通信方面表现出色,适合设备之间的快速数据交换和精准控制。而欧姆龙CJ1W-DRM21 模块基于DeviceNet通信协议,DeviceNet在工业现场总线领域应用广泛,侧重于设备…...
清华、智谱团队:「6000亿合成交错语音文本」预训练,问答性能提升近3倍
与基于文本的大语言模型(LLM)相比,语音语言模型(SpeechLM)接受语音输入并生成语音输出,能够实现更自然的人机交互。然而,传统的 SpeechLM 因缺乏无监督语音数据和并行语音-文本数据,…...
Python办公——openpyxl处理Excel每个sheet每行 修改为软雅黑9号剧中+边框线
目录 专栏导读背景1、库的介绍①:openpyxl 2、库的安装3、核心代码4、完整代码5、最快的方法(50万行44秒)——表头其余单元格都修改样式总结 专栏导读 🌸 欢迎来到Python办公自动化专栏—Python处理办公问题,解放您的双手 🏳️…...
遇到问题:hive中的数据库和sparksql 操作的数据库不是同一个。
遇到的问题: 1、hive中的数据库和sparksql 操作的数据库不同步。 观察上面的数据库看是否同步 !!! 2、查询服务器中MySQL中hive的数据库,发现创建的位置没有在hdfs上,而是在本地。 这个错误产生的原因是&…...
《网络攻防实践》实践五报告
1.实践内容 防火墙 (1)基本概念 所谓“防火墙”是指一种将内部网和公众访问网(如Internet)分开的方法,它实际上是一种建立在现代通信网络技术和信息安全技术基础上的应用性安全技术,隔离技术。越来…...
深入傅里叶级数与傅里叶变换:从基础到应用
傅里叶分析是数学、物理和工程领域的一项基础工具,其核心思想是将复杂的信号或函数分解为一系列简单的正弦和余弦函数的叠加。本文将带你从傅里叶级数入门,逐步深入傅里叶变换的概念及其应用场景。 一、傅里叶级数:周期信号的分解 1. 什么是傅…...
C++入门一
一、命名空间 #include <stdio.h> int rand 0; int main() {printf("hello world\n");printf("%d\n", rand); } 这样是可以运行的,可是当我们加入一个头文件的时候 #include <stdio.h> #include <stdlib.h> int rand 0;…...
Spring Boot 项目集成camunda流程引擎
Spring Boot 项目集成camunda流程引擎 camunda地址 camunda中文地址 使用camunda开源工作流引擎有:通过docker运行、使用springboot集成、部署camunda发行包、基于源代码编译运行等多种方式。 文本重点介绍如何在Spring Boot应用程序中如何集成Camunda Platform开…...
Ubuntu20.04编译安装Carla全过程
前言 Carla的安装是我现阶段解决的第一个问题,现记录一下我安装Carla的过程以及我在安装过程中遇到的一些问题。 一、安装前准备 1、硬件环境 carla是一款基于UE4开发的模拟仿真软件,本身对硬件的要求比较高。 我是windows与ubuntu双系统࿰…...
typecho 自动订阅 RSS
昨天学习了一下 RSS 订阅知识之后,经过一番百度搜索,终于在自己的博客上实现了 RSS 订阅功能,但苦于技术有限,不能对 Feed 文件进行定时缓存,每次打开链接都会比较延迟。今天继续对这个功能进行了学习,突然…...
MFC图形函数学习13——在图形界面输出文字
本篇是图形函数学习的最后一篇,相关内容暂告一段落。 在图形界面输出文字,涉及文字字体、大小、颜色、背景、显示等问题,完成这些需要系列函数的支持。下面做简要介绍。 一、输出文本函数 原型:virtual BOOL te…...
11.25.2024刷华为OD
文章目录 HJ76 尼科彻斯定理(观察题,不难)HJ77 火车进站(DFS)HJ91 走格子方法,(动态规划,递归,有代表性)HJ93 数组分组(递归)语法知识…...
Python练习55
Python日常练习 题目: 补充函数getLastDay(y,m),其功能是计算y年m月共有多少天。 --------------------------------------------------------- 注意: 部分源程序给出如下。请勿改动主函数main和其它函数中的 任何内容,…...
DDR5和DDR4之区别(The Difference between DDR5 and DDR4)
💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 DDR是什么意思? DDR5和D…...
分层架构 IM 系统之 Router 架构分析
通过前面文章的分析,我们已经明确,Router 的核心职责是作为中央存储记录在线客户端的连接状态,Router 在本质上是一个内存数据库。 内存是一种易失性的存储,既如此,Router 的可用性如何保障呢? 副本是分布…...
用函数实现模块化程序设计(七)--数组作为函数参数(排序算法)
调用有参函数时,需要实参,实参可以是常量,变量,表达式,数组元素的作用与变量相当,凡是变量出现的地方都可用数组代替,数组元素可以用作函数实参,数组名可以作实参和形参,…...
M31系列LoRa分布式IO模块功能简介
M31系列LoRa 分布式 IO 模块简介 M31系列LoRa分布式IO主机模块是一款强大的无线远程控制与采集设备,该设备采用 LoRa 无线技术(内置了无线模块),可通过串口或远程 LoRa 组网设备发送 Modbus RTU 指令进行控制,可搭配E…...
Dockerfile 安装echarts插件给java提供服务
java调用echarts插件,生成图片保存到磁盘然后插入到pptx中报表。 Dockerfile文件内容: #基础镜像,如果本地仓库没有,会从远程仓库拉取 openjdk:8 FROM docker.io/centos:centos7 #暴露端口 EXPOSE 9311 # 避免centos 日志输出 …...
学习threejs,使用VideoTexture实现视频Video更新纹理
👨⚕️ 主页: gis分享者 👨⚕️ 感谢各位大佬 点赞👍 收藏⭐ 留言📝 加关注✅! 👨⚕️ 收录于专栏:threejs gis工程师 文章目录 一、🍀前言1.1 ☘️VideoTexture 视频纹理 二、…...
【二分查找】Leetcode例题
【1】69. x 的平方根 - 力扣(LeetCode) 🍡解题思路:首先想到的是暴力查找,从1开始依次比较x与num*num的大小,然后找出满足num*num<x且(num1)*(num1)>x的num值;再来看看能不能优化一下&…...
稳定运行的以MySQL数据库为数据源和目标的ETL性能变差时提高性能方法和步骤
在ETL(Extract, Transform, Load)过程中,数据源和目标都为MySQL数据库时,性能变差可能由多种原因引起。提高以MySQL为数据源和目标的ETL性能需要综合考虑数据库性能、ETL任务的处理方式、硬件资源和工具的选择。通过批量处理、并行…...
Springboot(四十九)SpringBoot3整合jetcache缓存
上文中我们学习了springboot中缓存的基本使用。缓存分为本地caffeine缓存和远程redis缓存。现在有一个小小的问题,我想使用本地caffeine缓存和远程redis缓存组成二级缓存。还想保证他们的一致性,这个事情该怎么办呢? Jetcache框架为我们解决了这个问题。 JetCache是一个…...
JVM 参数前缀 -XX: 含义 详解
在 Java 虚拟机(JVM)中,参数前缀 -XX: 表示的是 JVM 的非标准(实验性)选项。这些参数用于调整和优化 JVM 的性能、垃圾回收行为、内存分配策略等。 1. 参数分类 -XX: 参数大致分为三类,根据其格式区分&…...
【Mac】安装Gradle
1、说明 Gradle 运行依赖 JVM,需要先安装JDK,Gradle 与 JDK的版本对应参见:Java Compatibility IDEA的版本也是有要求Gradle版本的,二者版本对应关系参见:Third-Party Software and Licenses 本次 Gradle 安装版本为…...