从零开始 C++----- 十五.一文吃透 C++ 异常:try-catch、栈展开、自定义异常、异常安全 noexcept 底层全剖析

目录

一、什么是异常?

[C 错误码 vs C++ 异常](#C 错误码 vs C++ 异常)

[1.1 异常核心定义](#1.1 异常核心定义)

[1.2 C 语言错误处理:错误码](#1.2 C 语言错误处理:错误码)

[1.3 C++ 异常优势](#1.3 C++ 异常优势)

[二、异常抛出 throw 与捕获 try-catch(基础语法 + 底层原理)](#二、异常抛出 throw 与捕获 try-catch(基础语法 + 底层原理))

[2.1 throw 抛出规则](#2.1 throw 抛出规则)

[1.throw 执行后,throw 后面所有代码永久不再执行,程序直接跳转匹配 catch](#1.throw 执行后,throw 后面所有代码永久不再执行,程序直接跳转匹配 catch)

[3. 编译器自动拷贝一份,放到全局异常缓冲区](#3. 编译器自动拷贝一份,放到全局异常缓冲区)

[4. catch 执行完,拷贝的副本自动销毁](#4. catch 执行完,拷贝的副本自动销毁)

[2.2 try-catch 完整语法结构](#2.2 try-catch 完整语法结构)

2.3除法异常案例

触发除零异常(b=0)执行分步流程

[三、重难点:栈展开(Stack Unwinding)分步详解](#三、重难点:栈展开(Stack Unwinding)分步详解)

[3.1 栈展开定义](#3.1 栈展开定义)

[3.2 标准执行步骤(固定流程)](#3.2 标准执行步骤(固定流程))

[3.3 栈展开两大关键副作用](#3.3 栈展开两大关键副作用)

[3.4 无匹配 catch 的后果](#3.4 无匹配 catch 的后果)

[四、异常匹配规则 + 自定义业务异常体系实战](#四、异常匹配规则 + 自定义业务异常体系实战)

[4.1 基础匹配规则](#4.1 基础匹配规则)

[4.2 允许隐式转换的 4 种特殊场景(面试高频)](#4.2 允许隐式转换的 4 种特殊场景(面试高频))

[4.3 多 catch 匹配优先级](#4.3 多 catch 匹配优先级)

[4.4 大型项目自定义异常完整代码(文档示例)](#4.4 大型项目自定义异常完整代码(文档示例))

[4.4.1 统一异常基类](#4.4.1 统一异常基类)

[4.4.2 各业务模块派生异常](#4.4.2 各业务模块派生异常)

[4.4.3 业务函数随机抛出对应异常](#4.4.3 业务函数随机抛出对应异常)

[4.4.4 main 捕获基类统一处理(多态)](#4.4.4 main 捕获基类统一处理(多态))

[4.4.5 设计思路分步讲解](#4.4.5 设计思路分步讲解)

[五、异常重新抛出 throw; 原理、场景、代码拆解](#五、异常重新抛出 throw; 原理、场景、代码拆解)

[5.1 使用场景](#5.1 使用场景)

[5.2 两种重抛语法核心区别(高频坑点)](#5.2 两种重抛语法核心区别(高频坑点))

​编辑

​编辑

出现的严重问题

[5.3 文档实战案例:消息发送重试逻辑分步拆解](#5.3 文档实战案例:消息发送重试逻辑分步拆解)

执行流程分步

六、异常安全:资源泄漏问题与解决方案

[6.1 问题根源](#6.1 问题根源)

[6.2 问题示例(文档代码)](#6.2 问题示例(文档代码))

问题拆解

[6.3 手动处理方案步骤](#6.3 手动处理方案步骤)

[6.4 最优工业方案:RAII(文档提及重点)](#6.4 最优工业方案:RAII(文档提及重点))

[6.5 析构函数抛异常禁忌(Effective C++ 条款 8)](#6.5 析构函数抛异常禁忌(Effective C++ 条款 8))

[七、异常规范:C++98 throw 与 C++11 noexcept](#七、异常规范:C++98 throw 与 C++11 noexcept)

[7.1 C++98 老式异常规范(已淘汰)](#7.1 C++98 老式异常规范(已淘汰))

[7.2 C++11 noexcept(主流标准)](#7.2 C++11 noexcept(主流标准))

[7.2.1 修饰函数 noexcept​编辑](#7.2.1 修饰函数 noexcept编辑)

[7.2.2 运算符 noexcept(表达式)](#7.2.2 运算符 noexcept(表达式))

[7.2.3 使用场景](#7.2.3 使用场景)

[八、C++ 标准库内置异常继承体系](#八、C++ 标准库内置异常继承体系)

[8.1 顶层基类 std::exception](#8.1 顶层基类 std::exception)

[8.2 两大分支分类](#8.2 两大分支分类)

[8.3 开发规范](#8.3 开发规范)

[九、全文总结 + 面试高频考点](#九、全文总结 + 面试高频考点)

[9.1 核心知识点总结](#9.1 核心知识点总结)


一、什么是异常?

C 错误码 vs C++ 异常

1.1 异常核心定义

异常机制作用:将错误检测代码 和 错误处理代码分离

  • 一段函数只负责识别运行时错误,抛出异常对象
  • 上层调用链函数负责处理错误,底层函数不需要关心上层如何处理

底层只管报错,上层负责收拾烂摊子

分层理解:

  • 底层函数 :只做一件事 ------ 判断有没有出错,出错直接 throw 抛出异常,完全不用管报错之后要怎么做。 不用打印日志、不用重试、不用弹窗提示、不用返回特殊错误码,报错流程全部丢给调用它的上层
  • 上层函数 :调用底层代码,用 try-catch 接住异常,在这里统一写错误处理逻辑
  • 独立模块之间通过异常对象传递错误信息,解耦代码

1.2 C 语言错误处理:错误码

  1. 实现方式:函数出错返回数字编号,开发者拿到编号后查表获取错误描述;
  2. 缺点:
    • 每次调用函数必须判断返回值,大量冗余 if 判断
    • 错误仅数字,无法携带上下文(出错位置、SQL、请求类型等)
    • 极易忘记判断返回值,漏处理错误,埋下线上隐患

1.3 C++ 异常优势

抛出的是对象,可以自定义类,封装错误信息、错误码、附加业务数据;信息完整,支持多态统一处理


二、异常抛出 throw 与捕获 try-catch(基础语法 + 底层原理)

2.1 throw 抛出规则

语法:throw 异常对象;

核心规则分步拆解

1. throw 执行后,throw 后面所有代码永久不再执行,程序直接跳转匹配 catch

2. 抛出局部对象会生成拷贝

①抛出的对象如果是函数内局部变量,函数栈销毁后对象会失效

抛出局部对象的致命问题:函数栈销毁,局部变量直接失效

基础栈知识铺垫:

函数里定义的变量(string err("出错了");)存在函数栈帧上。 函数执行完 / 异常触发栈展开时,整个栈帧会被销毁,栈上所有局部变量立刻调用析构、内存回收,变量变成野内存

举个反例场景:

如果编译器不做拷贝会发生什么:

  1. throw err执行,开始栈展开,销毁 test 函数栈帧
  2. 局部变量err被析构、内存释放
  3. 上层 catch 拿到的是一块已经回收的无效内存,读取会乱码、程序崩溃

这就是第一条说的:局部变量所在函数销毁后,原对象直接失效,没法给上层用

3. 编译器自动拷贝一份,放到全局异常缓冲区

为了解决上面的野内存问题,C++ 标准强制规定: 执行throw xxx时,编译器会调用拷贝构造函数,把你抛出的对象完整复制一份,存到一块独立、全局生命周期的内存(全局异常缓冲区)。

上面代码的真实执行流程:

string err("除零错误"); 栈上创建局部 err

throw err; → 编译器自动执行 string 全局副本 = err; 拷贝

全局副本放在堆 / 全局缓冲区,不受函数栈销毁影响

原局部 err 随 test 栈帧销毁,但是全局副本完好保留

不管中间多少层函数、栈怎么销毁,这份拷贝一直存在,直到 catch 处理

**4.**catch 执行完,拷贝的副本自动销毁

全局缓冲区里的副本不会永久占内存:

  • catch{}大括号内所有代码全部执行完毕,离开 catch 作用域;
  • 编译器自动调用副本的析构函数,释放全局缓冲区内存;
  • 括号里备注「类似函数传值返回」: 函数string func(){return s;}也是创建临时拷贝返回,临时对象用完自动销毁,逻辑一模一样。

②编译器自动生成异常对象拷贝,存放在全局异常缓冲区;

③catch 执行完毕后,拷贝的异常对象自动销毁(类似函数传值返回);

3.控制权跳转:从 throw 直接跳到匹配 catch,中间函数提前退出,栈上所有局部对象自动析构


2.2 try-catch 完整语法结构

规则:多个 catch 从上到下依次匹配,先匹配到优先执行


2.3除法异常案例

cpp 复制代码
#include <iostream>
#include <string>
using namespace std;

double Divide(int a, int b) {
    try {
        if (b == 0) {
            string s("Divide by zero condition!");
            throw s; // 抛出string类型异常对象
        } else {
            return ((double)a / (double)b);
        }
    }
    catch (int errid) { // 仅捕获int,和string不匹配
        cout << errid << endl;
    }
    return 0;
}

void Func() {
    int len, time;
    cin >> len >> time;
    try {
        cout << Divide(len, time) << endl;
    }
    catch (const char* errmsg) { // 仅捕获char*,和string不匹配
        cout << errmsg << endl;
    }
}

int main()
{
    while (1)
    {
        try
        {
            Func();
        }
        catch (const string& errmsg) // 匹配string异常
        {
            cout << errmsg << endl;
        }
    }
    return 0;
}
触发除零异常(b=0)执行分步流程
  1. Divide 函数内部执行throw string
  2. 检索当前函数 catch:catch(int) 类型不匹配,跳过
  3. 当前函数无匹配处理器,触发栈展开,退出 Divide 栈帧
  4. 回到上层 Func 函数的 try 块,检索catch(const char*)
  5. string 和 const char * 类型不匹配,再次栈展开,退出 Func 栈帧
  6. 回到 main 函数 try 块,匹配catch(const string&)
  7. 进入 catch 代码块打印错误信息,异常处理完成

三、重难点:栈展开(Stack Unwinding)分步详解

3.1 栈展开定义

抛出异常后,逐层销毁当前函数栈帧,向上遍历整个函数调用链,寻找匹配 catch 处理器的全过程,称为栈展开。

3.2 标准执行步骤(固定流程)

假设调用链:main() → func3() → func2() → func1(),异常在 func1 抛出

  1. 判断 throw 是否位于try{}代码块内;
  2. 遍历当前函数所有 catch 分支,判断是否存在类型匹配:
    • 存在匹配:执行对应 catch,栈展开终止;
    • 无匹配:销毁当前函数所有局部对象,退出当前栈帧;
  3. 回到上层调用函数,重复步骤 1、2;
  4. 持续向上遍历,直到 main 函数;
  5. main 函数仍无匹配 catch:调用标准库terminate(),程序直接强制终止

3.3 栈展开两大关键副作用

  1. 抛出点之后代码全部放弃执行,函数提前退出
  2. 每层栈帧内所有局部对象自动调用析构函数释放资源

3.4 无匹配 catch 的后果

异常一路栈展开到 main 仍找不到对应处理器 → 程序崩溃。 生产环境规范:main 函数末尾必须增加catch(...)兜底捕获


四、异常匹配规则 + 自定义业务异常体系实战

4.1 基础匹配规则

默认严格完全类型匹配 ,不会发生隐式转换。 例:throw string 无法被catch(char*)catch(int)捕获

4.2 允许隐式转换的 4 种特殊场景(面试高频)

  1. 非常量 → 常量:throw string 匹配 catch(const string&)(权限缩小,安全);
  2. 数组 → 数组元素指针:char buf[10]抛出,可被catch(const char*)捕获;
  3. 函数类型 → 函数指针;
  4. 派生类 → 基类(项目最常用) 业务统一异常基类,所有模块异常继承基类;捕获基类引用即可统一处理全部派生异常,配合虚函数实现多态打印错误

4.3 多 catch 匹配优先级

多个 catch 均可匹配异常时,离 throw 位置更近、代码书写靠前的 catch 优先执行。 避坑:捕获基类的 catch 必须写在所有派生类 catch 后面,否则派生异常会被基类提前拦截

4.4 大型项目自定义异常完整代码(文档示例)

4.4.1 统一异常基类

1.成员变量(protected 保护)

2.构造函数(public)

  • 参数 1 const string& errmsg:异常文字说明,const& 避免字符串拷贝,提升性能
  • 参数 2 int id:自定义错误编号
  • 初始化列表 :_errmsg(errmsg), _id(id):优先初始化成员变量,比函数体内赋值效率更高
  • 作用:抛出异常时 throw Exception("联机WebSocket断开", 1001); 快速创建异常对象

3.虚函数 virtual string what() const

  • virtual 虚函数 :核心设计点 ------ 支持多态重写 所有子类异常(比如 RoomExceptionCardParseException)可以重写 what(),追加专属信息(如房间 ID、卡牌 ID)
  • const:调用该函数不会修改类内成员变量,const 对象也能调用
  • 功能:返回异常文字描述,对标 C++ 标准库 std::exceptionwhat() 接口,统一使用习惯

4. 获取错误码接口 getid()

外部捕获异常后,用来拿到数字错误码,做分支处理:

4.4.2 各业务模块派生异常
cpp 复制代码
// 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;
};

// 缓存模块异常
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;
    }
};

// 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;
};
4.4.3 业务函数随机抛出对应异常
cpp 复制代码
void SQLMgr()
{
    if (rand() % 7 == 0)
    {
        throw SqlException("权限不足", 100, "select * from name = '张三'");
    }
    else
    {
        cout << "SQLMgr 调用成功" << endl;
    }
}

void CacheMgr()
{
    if (rand() % 5 == 0)
    {
        throw CacheException("权限不足", 100);
    }
    else if (rand() % 6 == 0)
    {
        throw CacheException("数据不存在", 101);
    }
    else
    {
        cout << "CacheMgr 调用成功" << endl;
    }
    SQLMgr();
}

void HttpServer()
{
    if (rand() % 3 == 0)
    {
        throw HttpException("请求资源不存在", 100, "get");
    }
    else if (rand() % 4 == 0)
    {
        throw HttpException("权限不足", 101, "post");
    }
    else
    {
        cout << "HttpServer调用成功" << endl;
    }
    CacheMgr();
}
4.4.4 main 捕获基类统一处理(多态)
cpp 复制代码
int main()
{
    srand(time(0));
    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;
}
4.4.5 设计思路分步讲解
  1. 基类Exception封装通用错误信息、错误码,虚函数what()预留重写接口
  2. 每个业务模块派生专属异常,扩展模块独有字段(SQL 语句、请求方式)
  3. 重写what()拼接模块专属报错信息,区分错误来源
  4. 底层业务函数根据场景抛出对应派生异常
  5. 上层仅捕获基类引用,利用多态自动调用子类what(),一行代码处理全部业务异常

五、异常重新抛出 throw; 原理、场景、代码拆解

5.1 使用场景

捕获异常后,局部做简单处理(重试、日志打印),但当前层级无法完全修复错误,需要将原始异常向上传递给外层调用者

5.2 两种重抛语法核心区别(高频坑点)

  1. throw;(推荐):不带参数,原样抛出捕获到的原始异常对象,保留派生类完整信息,不会发生对象切片
  2. throw e;(禁止):拷贝生成新对象,若 e 是基类引用,会发生对象切片,丢失子类独有数据,多态失效

前置铺垫:先搞懂场景

我们自定义一套异常继承体系: 基类 Exception,派生类 SqlException(子类独有字段:SQL 语句) 业务场景:底层抛出子类异常 → 中间层 catch 用基类引用接住,打印日志后想向上重抛给顶层处理

1. throw; 无参重抛(推荐,不会切片)

原理

不带参数的 throw; 会直接复用全局缓冲区里原始抛出的完整异常对象,不会拷贝新对象,完整保留子类全部信息

完整测试代码

执行结果

关键点

  1. throw; 不产生新拷贝,直接传递全局缓冲区里完整子类对象
  2. 基类引用绑定的子类信息完全保留,多态正常生效;
  3. 上层可以正常捕获派生类、访问子类独有成员,无信息丢失。

2. throw e; 带参数重抛(禁止,会发生对象切片)

什么是对象切片?

e基类引用 const Exception& e ,执行 throw e; 时,编译器会用基类拷贝构造函数,只复制基类部分成员,直接砍掉子类独有的全部数据 ,生成一个纯粹的基类临时对象。 子类独有的字段、重写虚函数全部丢失,多态失效,这就叫对象切片

改上面代码,把中间层重抛换成 throw e

执行结果

出现的严重问题

  1. 子类独有的 sql 字段直接被切掉、彻底丢失
  2. 虚函数不再走子类重写版本,多态失效
  3. 上层无法捕获派生类异常,只能捕获残缺基类,丢失关键业务信息
  4. 额外多一次对象拷贝,性能变差

5.3 文档实战案例:消息发送重试逻辑分步拆解

需求:网络波动(错误 id=102)最多重试 3 次;其他错误直接抛出给上层打印

cpp 复制代码
void _SeedMsg(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)
{
    // 最多尝试4次(0~3)
    for (size_t i = 0; i < 4; i++)
    {
        try
        {
            _SeedMsg(s);
            break; // 发送成功跳出循环
        }
        catch (const Exception& e)
        {
            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;
}
执行流程分步
  1. 循环最多 4 次调用发送接口_SeedMsg
  2. 抛出异常进入 catch,判断错误 id:
    • id=102(网络不稳):未到第 3 次打印重试;i=3 时throw;重抛
    • id=103(非好友):直接throw;,不重试
  3. main 捕获重抛的原始异常,打印完整错误信息

六、异常安全:资源泄漏问题与解决方案

6.1 问题根源

throw 会中断代码执行;若在new、锁、文件打开后、资源释放前抛出异常,释放代码不会执行,造成内存 / 句柄 / 锁泄漏

6.2 问题示例(文档代码)

cpp 复制代码
double Divide(int a, int b)
{
    if (b == 0)
    {
        throw "Division by zero condition!";
    }
    return (double)a / (double)b;
}

void Func()
{
    int* array = new int[10]; // 堆内存申请
    try
    {
        int len, time;
        cin >> len >> time;
        cout << Divide(len, time) << endl;
    }
    catch (...)
    {
        // 异常分支手动释放内存
        cout << "delete []" << array << endl;
        delete[] array;
        throw; // 重抛异常,交给上层处理
    }
    // 正常流程释放内存
    cout << "delete []" << array << endl;
    delete[] array;
}
问题拆解
  1. new int[10]申请堆内存
  2. Divide 触发除零异常,直接跳转到 catch
  3. 如果不写 catch 内 delete,函数末尾delete[]永远不会执行,内存泄漏

6.3 手动处理方案步骤

  1. 资源申请后全部业务代码放入 try 块
  2. catch(...)中手动释放所有已申请资源
  3. 使用throw;重抛异常,上层继续处理错误
  4. try 正常走完,函数末尾正常释放资源

6.4 最优工业方案:RAII(文档提及重点)

手动释放代码冗余、极易遗漏,RAII 资源获取即初始化彻底解决泄漏:

  • 资源封装到类,构造函数申请资源,析构函数释放资源
  • 栈展开时局部对象自动析构,无论是否抛异常,资源一定释放
  • 常用:unique_ptr/shared_ptr智能指针、std::lock_guard锁守卫

6.5 析构函数抛异常禁忌(Effective C++ 条款 8)

  1. 风险:析构执行时如果正在栈展开,此时再抛出异常会存在两个活跃异常
  2. C++ 标准行为:直接调用terminate()终止程序
  3. 规范:析构函数内部必须加 try-catch,就地处理所有异常,绝不向外抛出

七、异常规范:C++98 throw 与 C++11 noexcept

7.1 C++98 老式异常规范(已淘汰)

  1. void func() throw();:声明函数不会抛出任何异常
  2. void func() throw(bad_alloc);:声明仅会抛出 bad_alloc 类型异常; 缺点:语法繁琐、编译器不强制校验,工程几乎不使用

7.2 C++11 noexcept(主流标准)

7.2.1 修饰函数 noexcept

规则分步拆解:

  1. 含义:开发者承诺该函数不会抛出异常
  2. 编译行为:编译器不做强制校验,函数内部 throw 只会报警告,编译通过
  3. 运行行为:若 noexcept 修饰函数实际抛出异常,程序直接调用terminate()崩溃
7.2.2 运算符 noexcept(表达式)

编译期判断表达式是否可能抛出异常:

  • 表达式不会抛异常 → 返回 true (1);
  • 表达式可能抛异常 → 返回 false (0); 示例:
7.2.3 使用场景

标准库容器大量使用 noexcept,告知编译器函数无异常,编译器可做性能优化


八、C++ 标准库内置异常继承体系

8.1 顶层基类 std::exception

所有标准异常的父类,核心虚函数:

作用:返回异常描述字符串,派生类可重写自定义信息

8.2 两大分支分类

  1. std::logic_error 逻辑错误(代码编写错误,可提前规避)
    • invalid_argument:非法参数
    • length_error:容器长度超限
    • out_of_range:数组 / 容器下标越界
    • domain_error:数学定义域错误
  2. std::runtime_error 运行时错误(环境导致,无法提前预判)
    • bad_alloc:new 分配内存失败
    • bad_cast:dynamic_cast 转型失败
    • bad_typeid:空指针调用 typeid
    • overflow_error/underflow_error:数值溢出 / 下溢
    • range_error:数值区间错误

8.3 开发规范

main 函数外层捕获catch(const std::exception& e)拦截全部标准库异常,搭配catch(...)兜底未知异常


九、全文总结 + 面试高频考点

9.1 核心知识点总结

  1. 异常分离错误检测与处理,对比 C 错误码携带完整对象信息
  2. throw 中断后续代码,抛出对象生成拷贝,栈展开逐层销毁栈帧查找 catch
  3. 异常匹配默认严格类型匹配,派生类转基类是项目自定义异常核心设计
  4. throw;无参重抛保留原始异常,throw e会切片
  5. 异常中断执行易造成资源泄漏,RAII 是最优解决方案,析构禁止抛异常
  6. C++11 noexcept 声明无异常函数,运行抛异常直接 terminate
  7. 标准库异常以 std::exception 为基类,分 logic_error 和 runtime_error 两大分支
相关推荐
urkay-1 小时前
Kotlin Flow分类
android·开发语言·kotlin
gr95ZS6E61 小时前
基础入门java安全(一)--CC1基础分析
开发语言·python
智码看视界1 小时前
Day19 Spring Boot启动优化:启动时间从8秒降到2秒
java·spring boot·后端·启动优化
北京阿法龙科技有限公司2 小时前
AR智能眼镜安防应用核心指标:识别距离筑牢防线
java·开发语言·ar
杜子不疼.2 小时前
【C++】继承—C++的秘密武器,get父类的智慧
开发语言·c++
殘殤血2 小时前
Tomcat的事件监听机制:观察者模式 _
java·观察者模式·tomcat
汤姆yu2 小时前
面向具身智能的物理视频模拟器:蚂蚁灵波LingBot-Video开源模型全解析
java·大数据·人工智能·gpt·开源·大模型·音视频
SQL-First布道者2 小时前
Spring JDBC Ultra 十边型战士
java·spring boot·后端·mysql·spring·mybatis
殘殤血2 小时前
Tomcat的事件监听机制:观察者模式
java·观察者模式·tomcat