当前位置: 首页 > news >正文

【C++】—— 异常处理

前言:

  • 本期,我将给大家讲解的是有关 异常处理 的相关知识!

目录

(一)C语言传统的处理错误的方式

(二)C++异常概念

(三)异常的使用

1、异常的抛出和捕获

1️⃣ 异常的抛出和匹配原则 

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

2、异常的重新抛出 

3、异常安全

4、异常规范

(四)C++标准库的异常体系

(五)异常的优缺点

总结


(一)C语言传统的处理错误的方式

首先,我们回顾一下C语言处理异常的相关方式:

  •  终止程序,如assert,缺陷:用户难以接受。如发生内存错误,除0错误时就会终止程序。

下面是使用宏进行异常处理的简单代码描述:

#include <stdio.h>
#include <assert.h>int divide(int num1, int num2) 
{assert(num2 != 0);  // 断言num2不等于0return num1 / num2;
}int main() 
{int result = divide(10, 0);// 如果编译时定义了NDEBUG宏,assert会被禁用,否则会触发异常并终止程序执行return 0;
}

【解释说明】

1、在上述代码中,函数用于实现两个整数的除法运算。通过在函数内使用宏,可以进行条件判断,确保除数不为零。如果为零,会触发异常,终止程序的执行。

2、在函数中,调用函数并传递除数为0的情况。如果编译时未定义宏(即未启用调试模式),会触发异常并终止程序执行。如果定义了宏,则会被禁用,不会触发异常,程序会继续执行后续的代码。

【输出展示】


  • 返回错误码,缺陷:需要程序员自己去查找对应的错误。如系统的很多库的接口函数都是通过把错误码放到errno中,表示错误

 下面是使用返回错误码的方式处理异常的示例代码:

#include <stdio.h>
int divide(int num1, int num2, int* res)
{if (num2 == 0) {return -1; // 返回错误码 -1 表示除数为零的异常情况}*res = num1 / num2;return 0; // 返回 0 表示成功
}int main() 
{int num1 = 10, num2 = 0, res;int num = divide(num1, num2, &res);if (num != 0){printf("Error: Divide by zero\n");// 处理错误的逻辑}else {printf("Result: %d\n", res);// 处理正常情况的逻辑}return 0;
}

 【解释说明】

  1. 在函数中,调用函数并传递除数为0的情况。函数返回的错误码被存储在变量中,通过判断的值,可以确定是正常情况还是异常情况。如果不等于0,则表示发生了异常,可以根据具体情况进行错误处理。
  2. 如果返回的错误码为0,表示除法运算成功,可以通过变量获得计算结果,执行相应的正常处理逻辑。

【输出展示】

实际中C语言基本都是使用返回错误码的方式处理错误,部分情况下使用终止程序处理非常严重的错误。


(二)C++异常概念
 

在C++中,异常(Exception)是一种用于处理程序运行时错误的机制。异常提供了一种跳出正常程序流程的方式,将错误信息传递到适当的处理程序进行处理。

以下是关于C++异常的一些概念:

  1. 异常抛出:当发生异常情况时,可以使用throw 语句将异常抛出。throw 语句通常包含一个异常对象,该对象可以是基本类型、类对象或指针;

  2. 异常捕获:异常被抛出后,程序可以使用try-catch语句块来捕获并处理异常。try 块包含可能发生异常的代码,而catch块用于捕获和处理异常;

  3. 异常处理程序catch 块是用于处理异常的代码块。在catch 块中,可以根据抛出的异常类型来执行相应的处理逻辑。可以有多个 catch 块,按照顺序逐个匹配异常类型并执行匹配的处理逻辑。

如果有一个块抛出一个异常,捕获异常的方法会使用 try 和 catch 关键字。try 块中放置可能抛
出异常的代码,try 块中的代码被称为保护代码。

  • 使用 try/catch 语句的语法如下所示:
     
try
{// 保护的标识代码
}catch( ExceptionName e1 )
{// catch 块
}catch( ExceptionName e2 )
{// catch 块
}catch( ExceptionName eN )
{// catch 块
}

【小结】

  1. 通过合理使用异常处理机制,在程序中对错误进行捕获和处理,可以增加程序的健壮性和可维护性;
  2. 合适的异常处理可以使代码更清晰、可读性更好,并且可以更好地处理异常情况,提高程序的容错能力。

(三)异常的使用

1、异常的抛出和捕获

在C++中,异常的抛出和匹配原则遵循以下几个基本原则:

1️⃣ 异常的抛出和匹配原则
 

  1. 异常抛出:

    • 当程序发生异常情况时,可以使用throw语句将异常抛出。
    • throw语句通常包含一个异常对象,该对象可以是基本类型、类对象或指针。
  2. 异常匹配:

    • 异常的匹配是指根据抛出的异常类型来选择处理该异常的catch块。
    • C++的异常处理机制会按照顺序匹配try块中catch块的类型,找到能处理该异常类型的catch块。
  3. 异常类型匹配和继承关系:

    • C++允许异常类型形成继承关系,即派生类的异常对象可以被基类的catch块捕获。
    • 如果异常类型存在继承关系,派生类的catch块应该放在基类的catch块之前;否则,派生类的catch块将无法执行。
  4. 最匹配的异常处理:

    • C++异常处理机制会选择最匹配的catch块来处理抛出的异常。
    • 最匹配的catch块是指能够处理抛出的异常类型或其基类类型的catch块,即异常类型匹配的最接近情况。
  5. 异常未匹配的处理:

    • 如果在try块中抛出了异常,但没有找到匹配的catch块处理该异常,异常将传递到更高层的调用栈。
    • 如果异常一直没有被匹配的catch块处理,最终导致程序终止执行,并可能输出异常信息。

【注意事项】

  1. 异常的抛出和匹配原则是按照顺序匹配catch块来选择处理异常,因此在catch块的顺序布置上要谨慎;
  2. 通常,应从具体的异常类型开始,然后再向基类类型进行匹配,以确保异常可以被正确处理并执行相应的异常处理逻辑。

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

在函数调用链中,异常栈展开匹配原则主要指定了如何匹配异常类型并选择正确的异常处理代码。当异常发生时,C++运行时系统会从当前执行的函数开始,逐级检查调用栈中的函数调用,以查找与抛出的异常类型匹配的catch块。

以下是异常栈展开和匹配的原则:

  • 检查当前函数的try块:

    • 如果当前函数包含try块,运行时系统将查找匹配的catch块。
  • 检查当前函数的catch

    • 如果当前函数包含与抛出的异常类型匹配的catch块,那么该catch块将被执行。
    • 如果找到多个匹配的catch块,将选择最接近的(最近的)catch块来处理异常。
  • 如果当前函数没有匹配的catch

    • 异常栈展开到上一级调用函数。
    • 重复步骤 1 和步骤 2,直到找到匹配的catch块或达到调用栈的最顶层。
  • 如果在整个调用栈中没有找到匹配的catch

    • 程序的执行将终止,并调用标准库函数terminate()来终止程序。

 关于异常栈展开和匹配的重要注意事项:

  • 异常匹配时按照栈展开的顺序进行,而不是抛出异常的顺序。
  • 派生类的异常对象可以被基类的catch块捕获,因此在派生类的catch块之前应放置基类的catch块。
  • 如果在某个函数中抛出的异常没有匹配的catch块处理,异常会一直沿着调用栈向上传递,直到找到匹配的catch块或终止程序。
  • 异常的栈展开会跨越函数和线程边界,因此在多线程程序中也适用这些匹配原则。

 

例如以下示例:

 

接下来通过代码来具体的理解:

double Division(int a, int b) 
{if (b == 0)throw "Division by zero condition!";elsereturn ((double)a / (double)b);
}void Func() 
{int len, time;cin >> len >> time;cout << Division(len, time) << endl;
}int main() 
{try {Func();}catch (const char* errmsg) {cout << errmsg << endl;}catch (...) {cout << "unknown exception" << endl;}return 0;
}

输出展示:

 

 【解释说明】

  1. 通过异常处理机制来捕获并处理可能出现的除以零异常。当除以零发生时,会抛出一个字符串常量异常,并被catch (const char* errmsg)块捕获;
  2. 如果出现其他类型的异常,则被catch (...)块捕获并执行相应的处理逻辑。

 

2、异常的重新抛出
 

在C++中,异常的重新抛出允许在catch块内部对捕获的异常进行处理并将其重新抛出,以便让更高层的异常处理代码进一步处理该异常。可以使用throw语句将异常重新抛出。

以下是一个使用异常重新抛出的示例代码:

double Division(int a, int b)
{// 当b == 0时抛出异常if (b == 0){throw "Division by zero condition!";}return (double)a / (double)b;
}
void Func()
{// 这里可以看到如果发生除0错误抛出异常,另外下面的array没有得到释放。// 所以这里捕获异常后并不处理异常,异常还是交给外面处理,这里捕获了再// 重新抛出去。int* array = new int[10];try {int len, time;cin >> len >> time;cout << Division(len, time) << endl;}catch (...){cout << "delete []" << array << endl;delete[] array;throw;}// ...cout << "delete []" << array << endl;delete[] array;
}
int main()
{try{Func();}catch (const char* errmsg){cout << errmsg << endl;}return 0;
}

【解释说明】

  1. Func函数中,如果除以零的异常发生,异常将被捕获,并输出删除array的信息。然后,array会被释放(使用delete[]),并使用throw语句重新抛出异常。这样,异常会传递到更高层级的代码中。
  2. main函数中,异常被最外层的catch块捕获,并输出异常信息。
  3. 通过在Func函数中重新抛出异常,并在捕获异常之前及其后释放array,可以确保在异常传递给更高层级之前,已经释放了相关的资源。

【小结】

通过异常的重新抛出,可以在异常被捕获的地方对异常进行适当处理,并在更高层级的代码中继续处理相同的异常或进行其他操作。这种机制提供了灵活性和错误的向上传递。 


3、异常安全

  • 构造函数完成对象的构造和初始化最好不要在构造函数中抛出异常,否则可能导致对象不完整或没有完全初始化
  • 析构函数主要完成资源的清理,最好不要在析构函数内抛出异常,否则可能导致资源泄漏(内存泄漏、句柄未关闭等)
  • C++中异常经常会导致资源泄漏的问题,比如在new和delete中抛出了异常,导致内存泄漏,在lock和unlock之间抛出了异常导致死锁,C++经常使用RAII来解决以上问题,关于RAII我们智能指针这节进行讲解

4、异常规范

 在C++中,异常规范是一种在函数声明中指定函数可能抛出的异常的方式。异常规范可以作为函数的一部分,用于标识函数可能引发的异常类型。具体来说,异常规范指定了函数可抛出的异常类型列表。

在C++98\03 中,异常规范使用了throw()声明。例如:

void foo() throw(int, std::exception);

【解释说明】

  1. 上述代码表示函数foo可能抛出int类型和exception类型的异常;
  2. 如果函数抛出了未在异常规范中列出的其他异常类型,程序会调用unexpected函数,默认情况下会导致terminate被调用终止程序。

更多示例如下图所示: 

// 这里表示这个函数会抛出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 (std::size_t size, void* ptr) throw();

在C++11开始引入了更为灵活和安全的异常处理机制,即异常规范的替代方案:异常说明。异常说明使用noexcept关键字指定函数是否允许抛出异常。

使用noexcept关键字的函数可以被称为“noexcept函数”或“不抛异常函数”。它们在以下方面有一些重要的用途和优点:

  1. 优化性能:编译器可以基于对noexcept的显式承诺做出一些优化;
  2. 异常传播:有助于避免异常传播到不应该处理异常的上下文中;

下面是一些使用noexcept的示例:

void myFunction() noexcept {// 函数体,不会抛出异常
}void anotherFunction() {// 函数体,可能会抛出异常
}void myFunction2() noexcept(true) {// 与上面的 myFunction 等效,不会抛出异常
}void myFunction3() noexcept(false) {// 与 anotherFunction 等效,可能会抛出异常
}//不会抛出异常
thread (thread&& x) noexcept;

【注意事项】

  1. 在C++11中,noexcept关键字可以作为函数类型的一部分,标志着函数是否抛出异常;
  2. 在C++17之后,noexcept支持函数表达式,以动态地决定是否抛出异常。这使得异常规范在一些特定的情况下更加灵活和动态。

(四)C++标准库的异常体系

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

 

 

说明:实际中我们可以可以去继承exception类实现自己的异常类。但是实际中很多公司像上面一样自己定义一套异常继承体系。因为C++标准库设计的不够好用。
 

int main()
{try {vector<int> v(10, 5);// 这里如果系统内存不够也会抛异常v.reserve(1000000000);// 这里越界会抛异常v.at(10) = 100;}catch (const exception& e) // 这里捕获父类对象就可以{cout << e.what() << endl;}catch (...){cout << "Unkown Exception" << endl;}return 0;
}

(五)异常的优缺点

C++异常的优点:

  • 1. 异常对象定义好了,相比错误码的方式可以清晰准确的展示出错误的各种信息,甚至可以包含堆栈调用的信息,这样可以帮助更好的定位程序的bug。
  • 2. 返回错误码的传统方式有个很大的问题就是,在函数调用链中,深层的函数返回了错误,那么我们得层层返回错误,最外层才能拿到错误,具体看下面的详细解释。
// 1.下面这段伪代码我们可以看到ConnnectSql中出错了,先返回给ServerStart,
ServerStart再返回给main函数,main函数再针对问题处理具体的错误。
// 2.如果是异常体系,不管是ConnnectSql还是ServerStart及调用函数出错,都不用检查,因
为抛出的异常异常会直接跳到main函数中catch捕获的地方,main函数直接处理错误。
int ConnnectSql()
{// 用户名密码错误if (...)return 1;// 权限不足if (...)return 2;
}
int ServerStart() {if (int ret = ConnnectSql() < 0)return ret;int fd = socket()if(fd < 0)return errno;
}
int main()
{if (ServerStart() < 0)...return 0;
}
  • 3. 很多的第三方库都包含异常,比如boost、gtest、gmock等等常用的库,那么我们使用它们也需要使用异常。
  • 4. 部分函数使用异常更好处理,比如构造函数没有返回值,不方便使用错误码方式处理。比如T& operator这样的函数,如果pos越界了只能使用异常或者终止程序处理,没办法通过返回值表示错误。

C++异常的缺点:

  • 1. 异常会导致程序的执行流乱跳,并且非常的混乱,并且是运行时出错抛异常就会乱跳。这会导致我们跟踪调试时以及分析程序时,比较困难。
  • 2. 异常会有一些性能的开销。当然在现代硬件速度很快的情况下,这个影响基本忽略不计。
  • 3. C++没有垃圾回收机制,资源需要自己管理。有了异常非常容易导致内存泄漏、死锁等异常安全问题。这个需要使用RAII来处理资源的管理问题。学习成本较高。
  • 4. C++标准库的异常体系定义得不好,导致大家各自定义各自的异常体系,非常的混乱。
  • 5. 异常尽量规范使用,否则后果不堪设想,随意抛异常,外层捕获的用户苦不堪言。所以异常规范有两点:一、抛出异常类型都继承自一个基类。二、函数是否抛异常、抛什么异常,都使用 func() throw();的方式规范化。

总结

以上便是关于 c++11 有关异常的全部知识。接下来,简单的回顾下本文!!!

  1. 异常总体而言,利大于弊,所以工程中我们还是鼓励使用异常的;
  2. 另外OO的语言基本都是用异常处理错误,这也可以看出这是大势所趋。
     

到此,关于本篇便到此为止了。感谢大家的观看与支持!!!

相关文章:

【C++】—— 异常处理

前言&#xff1a; 本期&#xff0c;我将给大家讲解的是有关 异常处理 的相关知识&#xff01; 目录 &#xff08;一&#xff09;C语言传统的处理错误的方式 &#xff08;二&#xff09;C异常概念 &#xff08;三&#xff09;异常的使用 1、异常的抛出和捕获 1️⃣ 异常的…...

cookie和session是什么,区别是什么?

cookie HTTP 协议中的 Cookie 包括 Web Cookie 和浏览器 Cookie&#xff0c;它是服务器发送到 Web 浏览器的⼀ ⼩块数据。服务器发送到浏览器的 Cookie&#xff0c;浏览器会进⾏存储&#xff0c;并与下⼀个请求⼀起发送到服务器。通 常&#xff0c;它⽤于判断两个请求是否来⾃…...

linux的mmc子系统与块设备关联

1.前言 本文主要block组件的主要流程&#xff0c;在介绍的过程中&#xff0c;将详细说明和block相关的流程&#xff0c;涉及到其它组件的详细流程再在相关文章中说明。 2.主要数据结构和API 2.1 struct mmc_card Elemete Namestruct mmc_cardPathinclude/linux/mmc/card.hRe…...

【Spring MVC】

目录 &#x1f36e;1 什么是 MVC &#xff1f; &#x1f381;2 Spring MVC 的连接 &#x1f358;2.1 RequestMapping 实现 POST 和 GET 请求 &#x1f963;2.2 GetMapping 只支持 GET 请求 &#x1fad6;2.3 PostMapping 只支持 POST 请求 &#x1f36c;3 Spring MVC 获取参数的…...

【ES】笔记-生成器函数与调用

function* function* 这种声明方式 (function关键字后跟一个星号&#xff09;会定义一个生成器函数 (generator function)&#xff0c;它返回一个 Generator 对象。 function * gen(){console.log("heloo generator")}如何调用呢&#xff1f;先看下里面的内容 返回…...

将Spring Boot与Redis集成

一、引言 1、SpringBoot&#xff1a; Spring Boot是一个用于创建独立且可执行的Spring应用程序的框架。它简化了基于Spring框架的应用程序的开发过程&#xff0c;并提供了一种快速和简便的方式来构建Java应用程序。 Spring Boot提供了自动配置机制&#xff0c;通过引入适当的…...

vue echarts

安装 npm i -D echarts使用 <script setup lang"ts"> import * as echarts from echarts import { onMounted } from vueonMounted(() > {interface DataItem {value: [string, number]}let myChart echarts.init(document.getElementById(main))let dat…...

idea上利用JDBC连接MySQL数据库(8.1.0版)

1.了解jdbc概念 JDBC(Java DataBase Connectivity,java数据库连接)是一种用于执行SQL语句的Java API&#xff0c;可以为多种 关系数据库提供统一访问&#xff0c;它由一组用Java语言编写的类和接口组成。JDBC提供了一种基准&#xff0c;据此可以构建 更高级的工具和接口&#…...

【100天精通python】Day47:python网络编程_Web编程基础

目录 1 网络编程与web编程 1.1 网络编程 1.2 web编程 2 Web开发概述 3 Web开发基础 3.1 HTTP协议 3.2 Web服务器 3.3 前端基础 3.4 静态服务器 3.5 前后端交互的基本原理 4 WSGI接口 4.1 CGI 简介 4.2 WSGI 简介 4.3 定义 WSGI 接口 4.4 运行 WSGI 服务 4.5…...

DockerCompose介绍与使用

DockerCompose介绍与使用 1、DockerCompose介绍 DockerCompose用于定义和运行多容器 Docker 应用程序的工具。 通过 Compose可以使用 YAML 文件来配置应用程序需要的所有服务。一个使用Docker容器的应用&#xff0c;通常由多个容器组成&#xff0c;使用Docker Compose不再需要…...

Windows Qt 5.12.10下载与安装

Qt 入门实战教程&#xff08;目录&#xff09; C自学精简实践教程 目录(必读) 1 Qt5.12.10下载 qt-opensource-windows-x86-5.12.10.exe 官方离线安装包 Download Source Package Offline Installers | Qt 下载巨慢&#xff08;也可能很快&#xff09; 只能下载到最新的&…...

RustDesk最新版本编译与打包

本文环境 主要参考&#xff1a; https://www.yuque.com/shikangsi/efy0cp/wei3g1?https://blog.csdn.net/hualuohuakai2014/article/details/121605631 问题 flutter 生成 bridge 文件。 先安装工具&#xff0c;再生成ffi文件。 PS C:\Users\Administrator> cargo ins…...

Gin 框架入门实战系列(一)

GIN介绍 Gin是一个golang的微框架,封装比较优雅,API友好,源码注释比较明确,具有快速灵活,容错方便等特点 对于golang而言,web框架的依赖要远比Python,Java之类的要小。自身的net/http足够简单,性能也非常不错 借助框架开发,不仅可以省去很多常用的封装带来的时间,…...

【测试】pywinauto的简单使用(安装、常用对象、元素控件、鼠标操作、键盘操作)

1.说明 pywinauto是一个用于自动化Python 模块&#xff0c;适合Windows系统的软件&#xff08;GUI&#xff09;&#xff0c;可以通过Pywinauto遍历窗口&#xff08;对话框&#xff09;和窗口里的控件&#xff0c;也可以控制鼠标和键盘输入&#xff0c;所以它能做的事情比之前介…...

Java基础十八(正则表达式 + 日期时间)

1. 正则表达式 1.1 普通字符 字符描述示例[abc]匹配 […] 中所有字符[hlo] 匹配字符串 "hello world" 中所有的 h l o 字母[^ABC]匹配除了 […] 中所有字符[hlo] 匹配字符串 "hello world" 中除了 h l o 的所有字母[^a-z]匹配除了 […] 中所有字符[hlo] 匹…...

Linux C 多进程编程(面试考点)

嵌入式开发为什么要移植操作系统&#xff1f; 1.减小软硬件的耦合度&#xff0c;提高软件的移植性 2. 操作系统提供很多库和工具&#xff08;QT Open CV&#xff09;&#xff0c;提高开发效率 3.操作系统提供多任务机制&#xff0c;______________________? (提高C…...

c++一级

与7无关的数 #include<iostream> #include<iomanip> using namespace std; int main() {   int n,a,sum0,c0;   cin>>n;   for(int i1;i<n;i){     if(i%7!0){       ai;       c0; …...

Code Lab - 34

GAT里面有一些地方看的不是太懂&#xff08;GAT里Multi Attention的具体做法&#xff09;&#xff0c;暂时找了参考代码&#xff0c;留一个疑问 1. 一个通用的GNN Stack import torch_geometric import torch import torch_scatter import torch.nn as nn import torch.nn.fun…...

后端返回文件流,前端怎么导出、下载(8种方法可实现)

在前端导出和下载后端返回的文件流时&#xff0c;可以使用以下几种方法&#xff1a; 使用window.open()方法&#xff1a; 在前端使用window.open()方法打开一个新的窗口或标签页&#xff0c;并将后端返回的文件流作为URL传递给该方法。浏览器会自动下载该文件。例如&#xff1a…...

什么是 ThreadLocal?

ThreadLocal 是 Java 中的一个类,用于在多线程环境下,为每个线程提供独立的变量副本。每个线程可以通过 ThreadLocal 存储和获取数据,而不会影响其他线程的数据。这在某些情况下非常有用,特别是当多个线程需要访问共享数据,但又希望保持数据的隔离性时。 ThreadLocal 主要…...

椭圆曲线密码学(ECC)

一、ECC算法概述 椭圆曲线密码学&#xff08;Elliptic Curve Cryptography&#xff09;是基于椭圆曲线数学理论的公钥密码系统&#xff0c;由Neal Koblitz和Victor Miller在1985年独立提出。相比RSA&#xff0c;ECC在相同安全强度下密钥更短&#xff08;256位ECC ≈ 3072位RSA…...

JVM垃圾回收机制全解析

Java虚拟机&#xff08;JVM&#xff09;中的垃圾收集器&#xff08;Garbage Collector&#xff0c;简称GC&#xff09;是用于自动管理内存的机制。它负责识别和清除不再被程序使用的对象&#xff0c;从而释放内存空间&#xff0c;避免内存泄漏和内存溢出等问题。垃圾收集器在Ja…...

质量体系的重要

质量体系是为确保产品、服务或过程质量满足规定要求&#xff0c;由相互关联的要素构成的有机整体。其核心内容可归纳为以下五个方面&#xff1a; &#x1f3db;️ 一、组织架构与职责 质量体系明确组织内各部门、岗位的职责与权限&#xff0c;形成层级清晰的管理网络&#xf…...

Neo4j 集群管理:原理、技术与最佳实践深度解析

Neo4j 的集群技术是其企业级高可用性、可扩展性和容错能力的核心。通过深入分析官方文档,本文将系统阐述其集群管理的核心原理、关键技术、实用技巧和行业最佳实践。 Neo4j 的 Causal Clustering 架构提供了一个强大而灵活的基石,用于构建高可用、可扩展且一致的图数据库服务…...

安宝特方案丨船舶智造的“AR+AI+作业标准化管理解决方案”(装配)

船舶制造装配管理现状&#xff1a;装配工作依赖人工经验&#xff0c;装配工人凭借长期实践积累的操作技巧完成零部件组装。企业通常制定了装配作业指导书&#xff0c;但在实际执行中&#xff0c;工人对指导书的理解和遵循程度参差不齐。 船舶装配过程中的挑战与需求 挑战 (1…...

并发编程 - go版

1.并发编程基础概念 进程和线程 A. 进程是程序在操作系统中的一次执行过程&#xff0c;系统进行资源分配和调度的一个独立单位。B. 线程是进程的一个执行实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。C.一个进程可以创建和撤销多个线程;同一个进程中…...

MacOS下Homebrew国内镜像加速指南(2025最新国内镜像加速)

macos brew国内镜像加速方法 brew install 加速formula.jws.json下载慢加速 &#x1f37a; 最新版brew安装慢到怀疑人生&#xff1f;别怕&#xff0c;教你轻松起飞&#xff01; 最近Homebrew更新至最新版&#xff0c;每次执行 brew 命令时都会自动从官方地址 https://formulae.…...

TSN交换机正在重构工业网络,PROFINET和EtherCAT会被取代吗?

在工业自动化持续演进的今天&#xff0c;通信网络的角色正变得愈发关键。 2025年6月6日&#xff0c;为期三天的华南国际工业博览会在深圳国际会展中心&#xff08;宝安&#xff09;圆满落幕。作为国内工业通信领域的技术型企业&#xff0c;光路科技&#xff08;Fiberroad&…...

如何通过git命令查看项目连接的仓库地址?

要通过 Git 命令查看项目连接的仓库地址&#xff0c;您可以使用以下几种方法&#xff1a; 1. 查看所有远程仓库地址 使用 git remote -v 命令&#xff0c;它会显示项目中配置的所有远程仓库及其对应的 URL&#xff1a; git remote -v输出示例&#xff1a; origin https://…...

渗透实战PortSwigger Labs指南:自定义标签XSS和SVG XSS利用

阻止除自定义标签之外的所有标签 先输入一些标签测试&#xff0c;说是全部标签都被禁了 除了自定义的 自定义<my-tag onmouseoveralert(xss)> <my-tag idx onfocusalert(document.cookie) tabindex1> onfocus 当元素获得焦点时&#xff08;如通过点击或键盘导航&…...