C++11 异常
目录
0.前言
1.C语言传统错误处理方式
1.1使用返回值
1.2使用全局变量
1.3使用断言
1.4优缺点
2.C++异常的概念
3.异常的使用
3.1异常的抛出和捕获
3.1.1异常的抛出和匹配原则
3.1.2在函数调用链中异常栈展开匹配原则
3.2异常的重新抛出
3.3异常安全
3.4异常规范
4.自定义异常体系
5.C++标准库的异常体系
5.1标准库异常类的层次结构
5.2使用标准库异常类的优点
6.异常的优缺点
6.1C++异常的优点
6.2C++异常的缺点
7.结语
(图像由AI生成)
0.前言
在软件开发过程中,错误处理是不可避免的。有效的错误处理机制不仅能提高程序的健壮性,还能使代码更易于维护。C++11引入了强大的异常处理机制,使得程序员可以更优雅地处理异常情况。本文将详细介绍C++11中的异常处理机制,帮助读者更好地理解和应用这一特性。
1.C语言传统错误处理方式
在C语言中,错误处理主要通过返回值和错误码来实现。C语言没有内置的异常处理机制,因此程序员需要手动检查和处理每个可能出现的错误。以下是一些常见的错误处理方式:
1.1使用返回值
函数可以通过返回值来指示操作是否成功。如果操作失败,函数通常返回一个特定的错误码。调用者需要检查返回值并采取相应的措施。
#include <stdio.h>int divide(int a, int b, int *result) {if (b == 0) {return -1; // 返回错误码}*result = a / b;return 0; // 成功
}int main() {int res;if (divide(10, 2, &res) == 0) {printf("Result: %d\n", res);} else {printf("Error: Division by zero\n");}return 0;
}
在上面的例子中,divide
函数返回一个错误码,如果除数为零,则返回-1
表示错误。调用者通过检查返回值来确定是否发生了错误。
1.2使用全局变量
另一种方法是使用全局变量来存储错误码,例如errno
。这是C标准库提供的一个全局变量,用于表示最近一次函数调用中的错误代码。
#include <stdio.h>
#include <errno.h>int divide(int a, int b, int *result) {if (b == 0) {errno = EINVAL; // 设置错误码return -1;}*result = a / b;return 0;
}int main() {int res;if (divide(10, 0, &res) == 0) {printf("Result: %d\n", res);} else {perror("Error"); // 打印错误信息}return 0;
}
在这个例子中,当除数为零时,divide
函数设置全局变量errno
并返回错误。调用者使用perror
函数打印错误信息,该函数会根据errno
的值输出相应的错误描述。
1.3使用断言
对于某些情况下的错误处理,可以使用assert
宏来检查程序中的假设。如果条件为假,程序会打印错误信息并中止执行。这种方法通常用于开发和调试阶段。
#include <assert.h>int divide(int a, int b) {assert(b != 0); // 检查条件return a / b;
}int main() {int result = divide(10, 2);printf("Result: %d\n", result);return 0;
}
1.4优缺点
使用返回值和错误码进行错误处理有以下优缺点:
优点:
- 简单直观,易于理解和实现。
- 无需额外的语言支持,可以在任何C编译器上使用。
缺点:
- 错误码难以统一,容易引起混淆。
- 需要额外的代码来检查错误码,增加了代码的复杂度。
- 忽略错误检查可能导致程序运行错误。
- 错误处理分散在各个函数调用处,不利于维护和调试。
2.C++异常的概念
异常是一种处理错误的方式,当一个函数发现自己无法处理的错误时就可以抛出异常,让函数的直接或间接的调用者处理这个错误。
- throw: 当问题出现时,程序会抛出一个异常。这是通过使用
throw
关键字来完成的。 - catch: 在您想要处理问题的地方,通过异常处理程序捕获异常。
catch
关键字用于捕获异常,可以有多个catch
进行捕获。 - try:
try
块中的代码标识将被激活的特定异常,它后面通常跟着一个或多个catch
块。
如果有一个块抛出一个异常,捕获异常的方法会使用try
和catch
关键字。try
块中放置可能抛出异常的代码,try
块中的代码被称为保护代码。使用try/catch
语句的语法如下所示:
try {// 保护的标识代码
} catch (ExceptionName e1) {// catch 块
} catch (ExceptionName e2) {// catch 块
} catch (ExceptionName eN) {// catch 块
}
在C++中,异常处理机制使得错误处理更加结构化和集中化。throw
语句可以抛出任何类型的对象,通常是异常类的实例。标准库提供了多种异常类,可以直接使用,如std::exception
, std::runtime_error
, std::logic_error
等。用户还可以自定义异常类,以适应特定需求。
以下是一个简单的例子:
#include <iostream>
#include <stdexcept>void divide(int a, int b) {if (b == 0) {throw std::runtime_error("Division by zero");}std::cout << "Result: " << a / b << std::endl;
}int main() {try {divide(10, 0);} catch (const std::runtime_error &e) {std::cerr << "Error: " << e.what() << std::endl;}return 0;
}
在上面的例子中,如果除数为零,divide
函数会抛出一个std::runtime_error
异常。main
函数中的try
块捕获该异常,并输出错误信息。
3.异常的使用
3.1异常的抛出和捕获
3.1.1异常的抛出和匹配原则
- 抛出异常: 异常是通过抛出对象而引发的,该对象的类型决定了应激活哪个
catch
的处理代码。 - 选择处理代码: 被选中的处理代码是调用栈中与该对象类型匹配且位置最接近抛出异常位置的那一个。
- 异常对象的生成和销毁: 抛出异常对象后,会生成一个异常对象的拷贝,因为抛出的异常对象可能是一个临时对象,所以会生成一个拷贝对象,这个拷贝的临时对象会在被
catch
以后销毁。(这里的处理类似于函数的传值返回) - catch(...): 可以捕获任意类型的异常,问题是不知道异常错误是什么。
- 基类捕获: 实际中抛出和捕获的匹配原则有个例外,并不都是类型完全匹配,可以抛出其派生类对象,使用基类捕获,这个在实际中非常实用。
3.1.2在函数调用链中异常栈展开匹配原则
- 首先检查
throw
本身是否在try
块内部,如果是再查找匹配的catch
语句。如果有匹配的,则调用catch
的地方进行处理。 - 如果没有匹配的
catch
则退出当前函数栈,继续在调用函数的栈中进行查找匹配的catch
,不断重复上述过程。若到达main
函数的栈,依旧没有匹配的,则终止程序。 - 如果到达
main
函数的栈,依旧没有匹配的,则终止程序。 - 找到匹配的
catch
子句并处理以后,会继续沿catch
子句后面继续执行。
在下面的示例中,有三个函数func1()
, func2()
, func3()
。在func2()
中调用func1()
, func3()
中调用func2()
, main()
中调用func3()
。如果在func1()
中抛出一个异常,在main()
中用catch
语句捕获。
void func1() {throw std::runtime_error("Error occurred");
}void func2() {func1();
}void func3() {func2();
}int main() {try {func3();} catch (const std::runtime_error& e) {std::cerr << "Caught: " << e.what() << std::endl;}return 0;
}
栈展开过程如下:
- 首先检查
throw
本身是否在try
块内部,如果是再查找匹配的catch
语句。如果有匹配的,则处理。 - 如果没有匹配的
catch
则退出当前函数栈,继续在调用函数的栈中进行查找匹配的catch
,不断重复上述过程。若到达main
函数的栈,依旧没有匹配的,则终止程序。 - 找到匹配的
catch
子句并处理以后,会继续沿catch
子句后面继续执行。
通过这个过程,我们可以看到异常处理如何在调用链中展开,并最终被捕获和处理。
3.2异常的重新抛出
有可能单个的catch
不能完全处理一个异常,在进行一些校正处理以后,希望再交给更外层的调用链函数来处理,catch
则可以通过重新抛出将异常传递给更上层的函数进行处理。
重新抛出异常可以使用throw
关键字,这样可以将捕获的异常再抛出,以便在更高层次的catch
块中处理。这种方法对于需要在多个层次上处理异常的情况非常有用。
以下是一个例子,展示了如何重新抛出异常:
#include <iostream>
#include <stdexcept>// 第一级处理
void level1() {try {throw std::runtime_error("Error occurred at level 1");} catch (const std::runtime_error& e) {std::cerr << "Caught in level1: " << e.what() << std::endl;// 重新抛出异常throw;}
}// 第二级处理
void level2() {try {level1();} catch (const std::runtime_error& e) {std::cerr << "Caught in level2: " << e.what() << std::endl;// 再次重新抛出异常throw;}
}// 第三级处理(最高层次)
int main() {try {level2();} catch (const std::runtime_error& e) {std::cerr << "Caught in main: " << e.what() << std::endl;}return 0;
}
输出:
Caught in level1: Error occurred at level 1
Caught in level2: Error occurred at level 1
Caught in main: Error occurred at level 1
在这个例子中,异常首先在level1
函数中被抛出并捕获。level1
函数中的catch
块捕获异常后,输出错误信息并重新抛出异常。接着,level2
函数中的catch
块捕获重新抛出的异常,输出错误信息后再次重新抛出。最后,main
函数中的catch
块捕获到来自level2
的异常,并输出最终的错误信息。
这种方法确保了异常在不同的层次上得到处理,允许每个层次的代码对异常进行适当的处理和记录。
3.3异常安全
在编写C++代码时,异常安全性是一个非常重要的概念。它涉及确保程序在抛出异常时仍然保持一致的状态。实现异常安全性的方法主要包括以下几点:
-
构造函数完成对象的构造和初始化: 最好不要在构造函数中抛出异常,否则可能导致对象不完整或没有完全初始化。
-
析构函数主要完成资源的清理: 最好不要在析构函数内抛出异常,否则可能导致资源泄漏(例如内存泄漏、句柄未关闭等)。
C++中异常经常会导致资源泄漏的问题,比如在new
和delete
中抛出了异常,导致内存泄漏。在lock
和unlock
之间抛出了异常导致死锁。C++经常使用RAII(Resource Acquisition Is Initialization)来解决以上问题。RAII通过在对象的生命周期内绑定资源管理,以确保资源在对象销毁时被正确释放,从而避免资源泄漏和其他异常处理问题。
class Resource {
public:Resource() {// 构造函数中完成资源的分配和初始化resource_ = new int[100];}~Resource() {// 析构函数中完成资源的释放delete[] resource_;}
private:int* resource_;
};
在这个示例中,Resource
类的构造函数分配了资源,而析构函数负责释放资源,确保无论发生什么情况,资源都能得到正确的管理。
3.4异常规范
异常规范的目的是为了让函数使用者知道该函数可能抛出的异常有哪些。在C++98中,可以在函数的后面加上throw(类型)
,列出这个函数可能抛出的所有异常类型。C++11引入了noexcept
来表示函数不抛出异常。
// 这里表示这个函数会抛出A, B, C, D中的某种类型的异常
void fun() throw(A, B, C, D);// 这里表示这个函数会抛出bad_alloc的异常
void* operator new(std::size_t size) throw(std::bad_alloc);// 这里表示这个函数不会抛出异常
void operator delete(void* ptr) throw();// C++11中新增的noexcept,表示不会抛出异常
void thread() noexcept;
void thread(thread&&) noexcept;
在这个示例中,fun
函数可能抛出多种类型的异常,而operator new
函数可能抛出std::bad_alloc
异常。operator delete
函数和thread
函数则明确表示它们不会抛出任何异常。
4.自定义异常体系
在实际应用中,很多公司都会自定义自己的异常体系进行规范的异常管理。这是因为在一个项目中,如果每个人都随意抛出异常,那么外层的调用者就基本无法处理这些异常,程序的健壮性和可维护性会大大降低。因此,实际中通常会定义一套继承的规范体系,这样大家抛出的都是继承的派生类对象,捕获时只需要捕获一个基类即可。
自定义异常类示例
通常,自定义异常体系的做法是创建一个基类,并从这个基类派生出不同类型的异常类。这样可以在一个统一的框架内处理不同类型的异常。
#include <iostream>
#include <exception>// 自定义基类异常
class MyException : public std::exception {
public:virtual const char* what() const noexcept override {return "MyException occurred";}
};// 派生类异常类型1
class MyExceptionType1 : public MyException {
public:const char* what() const noexcept override {return "MyExceptionType1 occurred";}
};// 派生类异常类型2
class MyExceptionType2 : public MyException {
public:const char* what() const noexcept override {return "MyExceptionType2 occurred";}
};// 使用自定义异常体系
void functionThatThrows() {throw MyExceptionType1();
}int main() {try {functionThatThrows();} catch (const MyException& e) {std::cerr << "Caught: " << e.what() << std::endl;}return 0;
}
在这个示例中,MyException
是基类异常,MyExceptionType1
和MyExceptionType2
是从基类继承的派生类异常。通过这种方式,当发生异常时,调用者可以捕获基类MyException
,从而处理所有类型的派生类异常。
这种异常体系的优点是结构清晰,扩展性强,便于管理和维护。在实际应用中,自定义异常体系有助于规范异常处理流程,提高代码的可读性和可靠性。
5.C++标准库的异常体系
C++标准库提供了一套丰富的异常类体系,用于处理各种常见的错误情况。这些异常类大多数继承自std::exception
基类,并根据不同的错误类型派生出多个子类。了解和使用这些标准异常类,可以使代码更具可读性和一致性。
5.1标准库异常类的层次结构
C++标准库的异常类层次结构如下:
-
std::exception: 所有标准库异常的基类。它定义了一个虚函数
what()
,用于返回异常的描述信息。 -
std::logic_error: 继承自
std::exception
,表示程序逻辑错误。常见的派生类包括:std::invalid_argument
: 表示无效参数。std::domain_error
: 表示参数超出定义域。std::length_error
: 表示长度错误。std::out_of_range
: 表示超出范围。
-
std::runtime_error: 继承自
std::exception
,表示程序运行时错误。常见的派生类包括:std::range_error
: 表示范围错误。std::overflow_error
: 表示算术溢出错误。std::underflow_error
: 表示算术下溢错误。
以下是使用标准库异常类的示例:
#include <iostream>
#include <stdexcept>void functionThatThrows() {throw std::invalid_argument("Invalid argument provided");
}int main() {try {functionThatThrows();} catch (const std::invalid_argument& e) {std::cerr << "Caught std::invalid_argument: " << e.what() << std::endl;} catch (const std::exception& e) {std::cerr << "Caught std::exception: " << e.what() << std::endl;}return 0;
}
在这个示例中,functionThatThrows
函数抛出了一个std::invalid_argument
异常。在main
函数中,使用try-catch
块捕获该异常并输出相应的错误信息。
5.2使用标准库异常类的优点
- 一致性: 使用标准库异常类,可以使代码在处理异常时保持一致性,减少混乱和错误。
- 可读性: 标准库异常类名称直观,能够明确表达异常的含义,提高代码的可读性。
- 复用性: 标准库异常类经过充分测试和验证,具有高可靠性,减少了自定义异常类的工作量。
6.异常的优缺点
6.1C++异常的优点
-
精确定位错误: 异常处理机制可以清晰准确地展示出错误的各种信息,甚至可以包含堆栈调用的信息,这样有助于更好地定位程序的bug。
-
简化错误处理流程: 使用返回值来传递错误信息在深层的函数调用链中会变得非常复杂。相比之下,异常处理机制可以直接将异常传递到最顶层的捕获块,无需逐层检查错误返回值。
示例代码:
int ConnectSql() {// 用户名密码错误if (...) return 1;// 权限不足if (...) return 2; }int ServerStart() {int ret = ConnectSql();if (ret < 0)return ret;int fd = socket();if (fd < 0)return errno;return 0; }int main() {int result = ServerStart();if (result < 0) {std::cerr << "Error: " << result << std::endl;return result;}// 其他逻辑return 0; }
使用异常处理机制后的代码:
class MyException : public std::exception { public:const char* what() const noexcept override {return "MyException occurred";} };void ConnectSql() {throw MyException(); }void ServerStart() {ConnectSql(); }int main() {try {ServerStart();} catch (const MyException& e) {std::cerr << "Caught: " << e.what() << std::endl;}// 其他逻辑return 0; }
-
支持第三方库的异常处理: 很多第三方库(如Boost, GTest, GMock等)都使用异常来报告错误。使用这些库时,异常处理可以无缝衔接。
-
适合部分函数的错误处理: 一些函数(例如构造函数)没有返回值,因此无法使用错误码方式处理错误。使用异常可以解决这个问题。
6.2C++异常的缺点
-
执行流混乱: 异常会导致程序的执行流突然跳转,使代码的控制流变得复杂。这在调试和分析程序时会增加难度。
-
性能开销: 虽然在现代硬件上这个影响可以忽略不计,但异常处理机制确实会带来一定的性能开销。
-
资源管理复杂: C++没有垃圾回收机制,资源需要手动管理。如果异常处理不当,可能会导致资源泄漏和死锁等问题。这需要使用RAII(资源获取即初始化)模式来管理资源,但学习成本较高。
-
异常体系复杂: C++标准库的异常体系定义复杂且难以掌握,导致很多开发者选择自己定义异常体系,增加了代码的混乱度。
-
滥用异常: 不当的异常使用会导致代码难以维护和理解。因此,使用异常时需要遵循一定的规范:抛出异常类应继承自一个基类;尽量明确函数会抛出哪些异常。
总的来说,异常处理机制的优点远大于其缺点,因此在工程实践中我们鼓励使用异常处理。另外,面向对象的编程语言基本都支持异常处理,这也表明异常处理是一个发展趋势。通过正确使用异常处理机制,可以大大提高代码的健壮性和可维护性。
7.结语
C++11的异常处理机制为程序员提供了一种强大的工具,用于处理程序中的错误和异常。通过使用异常,我们可以实现更加清晰、结构化和易于维护的代码,显著提升程序的健壮性和可读性。尽管异常处理机制有其复杂性和潜在的性能开销,但其优势远远超过了这些缺点。掌握并合理使用C++11的异常处理机制,将使我们的开发工作更加高效和可靠。希望本文能帮助读者更好地理解和应用C++11的异常处理特性,从而编写出更优秀的程序。
相关文章:

C++11 异常
目录 0.前言 1.C语言传统错误处理方式 1.1使用返回值 1.2使用全局变量 1.3使用断言 1.4优缺点 2.C异常的概念 3.异常的使用 3.1异常的抛出和捕获 3.1.1异常的抛出和匹配原则 3.1.2在函数调用链中异常栈展开匹配原则 3.2异常的重新抛出 3.3异常安全 3.4异常规范 4.自定义异常体系…...

pip下载lap失败
把pip install lap改为pip intsall lapx...

【Material-UI】Button 中的点击事件处理(Handling clicks)详解
文章目录 一、点击事件处理基础1. 基本用法2. 事件处理器的传递 二、实际应用中的注意事项1. 事件处理逻辑的优化2. 避免过多的状态更新3. 使用合适的事件类型 三、关于文档中未提及的原生属性四、最佳实践1. 无障碍性2. 视觉反馈3. 防止重复点击 五、总结 在现代前端开发中&am…...

Spring Cache框架(AOP思想)+ Redis实现数据缓存
文章目录 1 简介1.1 基本介绍1.2 为什么要用 Spring Cache? 2 使用方法2.1 依赖导入(Maven)2.2 常用注解2.3 使用步骤2.4 常用注解说明1)EnableCaching2)CachePut3)Cacheable4)CacheEvict 3 注意…...

在Windows编程中,MFC\C++中如何在OnCopyData中传递Vector类型数据?
我们在通过 WM_COPYDATA 消息实现进程间通信时,发送char 数组或其他类型数组与发送vector是有区别的。 1、发送基础类型时,直接发送指针。 typedef struct tagMYSTRUCT {int nTest;wchar_t cTest[40] {0}; } MYSTRUCT, *PMYSTRUCT;MYSTRUCT stSend; s…...

Java常见面试题-01-java基础
文章目录 面向对象的特征Java 的基本数据类型有哪些JDK、JRE、JVM 的区别重载和重写的区别Java 中和 equals 的区别String、StringBuffer、StringBuilder 三者之间的区别接口和抽象类的区别是什么string 常用的方法有哪些什么是单例模式?有几种?什么是反…...

Python爬虫实战:利用代理IP爬取百度翻译
文章目录 一、爬取目标二、环境准备三、代理IP获取3.1 爬虫和代理IP的关系3.2 巨量IP介绍3.3 超值企业极速池推荐3.4 IP领取3.5 代码获取IP 四、爬虫代码实战4.1分析网页4.2 寻找接口4.3 参数构建4.4 完整代码 一、爬取目标 本次目标网站:百度翻译(http…...

Transformer学习之DETR
文章目录 1.算法简介1.1 算法主要贡献1.2 算法网络结构 2.损失函数设计2.1 二分图匹配(匈牙利算法)2.2 二分图匹配Loss_match2.3 训练Loss_Hungarian 3.网络核心模块3.1 BackBone模块3.2 空间位置编码(spatial positional encoding)3.2.1 输入与输出3.2.2 空间位置编码原理 3.3…...

场外个股期权是什么品种?可以交易哪些品种?
今天带你了解场外个股期权是什么品种?可以交易哪些品种?场外个股期权是指在场外市场进行交易的个股期权合约,与在交易所交易的标准化个股期权有所不同,它是由买方和卖方通过私下协商,而非通过公开交易所进行买卖和定价…...

每日学术速递8.5-3
1.BoostMVSNeRFs: Boosting MVS-based NeRFs to Generalizable View Synthesis in Large-scale Scenes 标题: BoostMVSNeRFs:将基于 MVS 的 NeRFs 提升到大规模场景中的可泛化视图合成 作者:Chih-Hai Su, Chih-Yao Hu, Shr-Ruei Tsai, Jie-…...

C#针对kernel32.dll的一些常规使用
1、前言 Window是一个复杂的系统,kernel32是一个操作系统的核心动态链接库文件。它提供了大量的API函数,提供了操作系统的基本功能。 2、Ini使用 Ini文件读写使用时,我们需要用到其中的一些函数对文件进行读写。 API: /// &l…...

电话营销机器人的优势
在人工智能的新趋势下,企业开始放弃传统外呼系统,转而使用电话销售机器人,那么使用机器人比坐席手动外呼好吗,真的可以代替人工坐席外呼吗,效率真的高吗? 1、 真人式语音 电话销售人员可以将自定义的话术…...

Oracle SQL Developer 连接第三方数据库
首先Oracle SQL Developer除了支持连接Oracle数据库外,还支持连接第三方数据库,包括: Amazon RedshiftHiveIBM DB2MySQLMicrosoft SQL ServerSybase Adaptive ServerPostgreSQLTeradataTimesTen 首先,你需要在菜单Tools > Pr…...

OSPF路由协议多区域
一、OSPF路由协议单区域的弊端 1、LSDB庞大,占用内存大,SPF计算开销大; 2、LSA洪泛范围大,拓扑变化影响范围大; 3、路由不能被汇总,路由表庞大,查找路由开销大。 二、如何解决OSPF单区域的问题? 引入划分区域 1、每个区域独立存储LSDB,划分区域减小了LSDB。 2、…...

8.5 C++
思维导图 试编程 提示并输入一个字符串,统计该字符中大写、小写字母个数、数字个数、空格个数以及其他字符个数 要求使用C风格字符串完成 #include <iostream> #include <array>using namespace std;int main() {cout << "请输入一个字符…...

MySQL —— 初始数据库
数据库概念 在学习数据库之前,大家保存数据要么是在程序运行期间,例如:在学习编程语言的时候,大家写过的管理系统,运用一些简单的数据结构(例如顺序表)来组织数据,可是程序一旦结束…...

【JVM】垃圾回收机制、算法和垃圾回收器
什么是垃圾回收机制 为了让程序员更加专注于代码的实现,而不用过多的考虑内存释放的问题,所以在Java语言中,有了自动的垃圾回收机制,也是我们常常提及的GC(Garbage Collection) 有了这个垃圾回收机制之后,程序员只需…...

大数据资源平台建设可行性研究方案(58页PPT)
方案介绍: 在当今信息化高速发展的时代,大数据已成为推动各行各业创新与转型的关键力量。为了充分利用大数据的潜在价值,构建一个高效、安全、可扩展的大数据资源平台显得尤为重要。通过本方案的实施企业可以显著提升数据处理能力、优化资源配置、促进业…...

PHP教育培训小程序系统源码
🚀【学习新纪元】解锁教育培训小程序的无限可能✨ 📚 引言:教育培训新风尚,小程序来引领! Hey小伙伴们,是不是还在为找不到合适的学习资源而烦恼?或是厌倦了传统教育模式的单调?今…...

吴恩达机器学习笔记
1.机器学习定义: 机器学习就是让机器从大量的数据集中学习,进而得到一个更加符合现实规律的模型,通过对模型的使用使得机器比以往表现的更好 2.监督学习: 从给定的训练数据集中学习出一个函数(模型参数)…...

React和Vue3 的 Diff 算法有什么区别
React 和 Vue 3 的 Diff 算法都有相似的目标,即在组件状态或属性变化时高效地更新 DOM,但它们在实现细节上有所不同。以下是 React 和 Vue 3 的 Diff 算法的主要区别: React 的 Diff 算法 1. 同层比较 React 使用的是同层比较策略…...

【vulhub靶场之rsync关】
一、使用nmap模块查看该ip地址有没有Rsync未授权访问漏洞 nmap -p 873 --script rsync-list-modules 加IP地址 查看到是有漏洞的模块的 二、使用rsync命令连接并读取文件 查看src目录里面的信息。 三、对系统中的敏感文件进行下载——/etc/passwd 执行命令: rsy…...

全球7大高质量海外代理IP对比大全
随着国内市场逐渐饱和,越来越多朋友私信我说打算开拓海外市场这片蓝海了!海外代理IP作为解决这些需求的有效工具,帮助跨境企业或团队进行社媒管理、电商运营、市场调研、抓取数据、广告验证等等业务。但是,市场上提供的代理IP服务…...

对于原型链的理解
1.同一个构造函数的多个实例之间 无法共享属性(创建多个实例的时候会造成资源浪费) function Cat(name, color) {this.name name;this.color color;this.meow function () {console.log(miao);}; }var cat1 new Cat(LH, White); var cat2 new Cat(…...

Web开发:Vue中的事件小结
一、全选按钮checkbox <template><div id"checkboxs"><label><input id"selectAll" type"checkbox" v-model"selectAllChecked" change"selectAllItems">全部</label><label v-for"…...

基于Springboot的运行时动态可调的定时任务
配置类 import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;Configuration public class TaskSchedulerConfig {Bean(destroyMe…...

linux perf
perf是Linux性能分析工具的集合,它提供了丰富的命令来收集和分析程序运行时的性能数据。perf能够报告CPU使用率、缓存命中率、分支预测成功率等多种硬件级别的事件,同时也支持软件级别的事件,如页面错误、任务切换等。perf是理解程序性能瓶颈…...

Linux--网络层IP
IP协议 IP协议,全称Internet Protocol(互联网协议),是TCP/IP协议族中的核心协议之一,用于在互联网络上进行数据的传输。IP协议的主要功能是确保数据从一个网络节点(如计算机、服务器、路由器等)…...

浅谈vite之import.meta
一. 解析 开发者使用一个模块时,有时需要知道模板本身的一些信息(比如模块的路径)。现在有一个提案,为 import 命令添加了一个元属性 import.meta,返回当前模块的元信息。 import.meta只能在模块内部使用,如…...

【Pytorch实用教程】Pytorch中nn.Sequential的用法
nn.Sequential 是 PyTorch 中用于构建神经网络的一种容器类,它可以按顺序封装多个子模块(层),并依次将输入数据传递给这些子模块。这样可以简化模型的定义,使得代码更加简洁和易读。 文章目录 基本用法方法一:直接传递子模块方法二:使用 `OrderedDict`动态构建模型优点注…...