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

【C++】异常 智能指针

C++异常 & 智能指针

  • 1.C++异常
    • 1.1.异常的抛出与捕获
    • 1.2.异常体系
    • 1.3.异常安全与规范
    • 1.4.异常优缺点
  • 2.智能指针
    • 2.1.RAII
    • 2.2.智能指针的使用及原理
      • 2.2.1.auto_ptr
      • 2.2.2.unique_ptr
      • 2.2.3.shared_ptr
      • 2.2.4.shared_ptr的循环引用问题 & weak_ptr
    • 2.3.定制删除器

1.C++异常

C++异常是一种处理错误的方式。当一个函数遇到自己无法处理的错误时就可以抛出异常,让该函数的直接或间接调用者通过捕获这个异常来处理错误。
一些异常关键字的介绍:

  • throw:程序是通过throw关键字来抛出异常的
  • catch:异常要通过catch关键字来进行捕获
  • trytry{}中的程序可能会抛出异常
// 异常捕获的基本语法如下
try
{// ...
}
catch(Exception_1 e)
{// ...
}
catch(Exception_2 e)
{// ...
}
...

1.1.异常的抛出与捕获

异常是以对象形式抛出的,当throwtry{}里时,异常抛出,会首先查找匹配的catch。而且该对象的类型决定了由哪个catch进行捕获处理。而被选中的catch处理程序是与对象类型匹配且离异常抛出位置最近的一个。
如果没有匹配的catch则会跳出当前函数栈,在调用该函数的栈中查找匹配的catch。如果直到main函数栈帧都没有匹配的catch,程序则会终止。
异常对象可能是一个临时对象,在被抛出时出作用域会被销毁,所以异常对象的抛出会生成一个拷贝被catch捕获。
catch(...)语句可以捕获任意类型的对象,但是无法确定捕获的错误异常类型是什么。实际中通常会最后再加上一个catch(...),防止程序直接终止。匹配合适的catch处理后,会继续沿着catch语句后面的程序执行。
抛异常时可以拋任意类型的对象,捕获时要求类型匹配。但实际中,抛出的异常对象类型并不一定要求和catch类型完全匹配。因为可以抛出派生类对象,使用基类进行捕获。

异常有时候也会被重新抛出,当前catch不能完全处理这样一个异常,在完成一些校正处理后,通过throw重新抛出给更上层的catch处理。

class Exception
{
public:Exception(const string& errmsg, int errid): _errmsg(errmsg), _errid(errid){}virtual string what() const{return _errmsg;}virtual int get() const{return _errid;}
protected:string _errmsg;int _errid;
};class HttpServerException : public Exception
{
public:HttpServerException(const string& errmsg, int id, const string& type): Exception(errmsg, id), _type(type){}virtual string what() const{return "HttpServerException:" + _type + ":" + _errmsg;}
protected:string _type;
};void SendMsg(const string& str)
{srand((unsigned int)time(0));if (rand() % 3 == 0){throw HttpServerException("网络错误", 333, "get");}else if (rand() % 4 == 0){throw HttpServerException("权限不足", 444, "post");}cout << str << "发送成功" << endl;
}void Server()
{string str = "hello world";// 出现网络错误,重试3次int count = 3;while (count--){try{SendMsg(str);// 程序走到此处,说明没有发生异常break;}catch (const Exception& e){if (e.get() == 333 && count > 0){continue;}else{throw e; // 异常重新抛出}}}
}void Test1()
{while (true){try{Server();}catch (const Exception& e){// 多态cout << e.what() << endl;}catch (...){cout << "Unkown Exception" << endl;}Sleep(1000);}
}

1.2.异常体系

C++本身是提供了一系列标准的异常的,这些异常是以父子类层次结构体系组织起来的,我们可以在程序中直接使用这些标准的异常,抛各种子类对象的异常,用一个父类对象进行接收。
在这里插入图片描述
我们也可以去继承execption类来实现自己的异常类。但实际中很多公司会定义一套自己的异常继承体系,一方面是为了规范的异常管理,另一方面C++标准异常还是不够好用。

1.3.异常安全与规范

可以在一个函数的后面接 throw(type...),列出这个函数可能抛出的异常类型有哪些。
如果函数后面接的是throw(),表示函数不抛异常。C++11新增了noexcept,用于表示不会抛异常。

构造函数中最好不要抛异常,否则可能导致对象构造不完整。
析构函数中最好不要抛异常,否则可能导致资源泄露。

1.4.异常优缺点

C语言处理错误的方式有两种:

  • assert终止程序。
  • 返回错误码errno

实际中C语言基本都是使用错误码的方式处理错误,有时候会使用终止程序的方式来处理非常严重的错误。
C++异常的优势主要是相对C语言而言的。

  • 异常对象相比错误码的方式可以展示出错误的各种信息,来帮助更好的定位程序的bug。
  • 错误码的使用有一个很大的问题就是,在函数调用层次中,深层的函数返回错误得层层返回。而异常可以直接跳到catch的地方。

异常也有缺点。

  • 异常可能导致程序的执行流乱跳,使得跟踪调试以及分析程序时更麻烦。
  • 异常很容易导致内存泄漏、死锁等安全问题。但这个基本可以通过RAII处理。

但异常总体而言利大于弊,另外面向对象的语言基本都是使用异常处理错误的,这也是大势所趋。

2.智能指针

int div()
{int a, b;cin >> a >> b;if (b == 0)throw invalid_argument("Division by zero error");return a / b;
}void Func()
{// 1、如果p1处new 抛异常会如何?// 2、如果p2处new 抛异常会如何?// 3、如果div调用处又抛异常会如何?int* p1 = new int;int* p2 = new int;cout << div() << endl;delete p1;delete p2;
}void Test2()
{try{Func();}catch (exception& e){cout << e.what() << endl;}
}

通过这个程序我们可以看到,p1抛异常时没什么问题,p1p2都没创建出来;
p2抛异常时,p1创建出来了,但没有释放,导致内存泄露了;
div()抛异常时,p1p2都创建出来了,但都没有释放,导致内存泄露了。
这里简单介绍一下内存泄漏的知识。
内存泄漏是指因为人为的疏忽或错误,而造成的程序未能释放已经不再使用的内存的情况。
内存泄漏并不是物理上内存的消失,而是由于程序失去了对该段内存的控制管理,而造成的内存的浪费。
长期运行的程序如果存在内存泄漏,会导致服务响应越来越慢,直至最终卡死。
C/C++程序一般关注两种方式的内存泄漏:堆内存泄露 和 系统资源泄露。
而内存泄漏的解决方式也分为两种:

  • 事前预防型,如智能指针的使用。
  • 事后差错型,如使用内存泄漏工具进行检测。

2.1.RAII

RAII(Resource Acquisition Is Initialization) - 资源获得即初始化。
RAII是一种通过利用对象生命周期来控制程序资源的简单技术。
在对象构造时获取资源,使控制的资源在对象生命周期内始终保持有效,最后在对象析构时释放资源。
这样做就不需要显式地释放资源,而且对象所需的资源在其生命周期内始终保持有效。

template<class T>
class SmartPtr
{
public:SmartPtr(T* ptr): _ptr(ptr){}~SmartPtr(){cout << "~SmartPtr()" << endl;delete _ptr;}
private:T* _ptr;
};int div()
{int a, b;cin >> a >> b;if (b == 0)throw invalid_argument("Division by zero error");return a / b;
}void Func()
{int* p1 = new int;SmartPtr<int> sp1(p1);int* p2 = new int;SmartPtr<int> sp2(p2);cout << div() << endl;
}void Test3()
{try{Func();}catch (exception& e){cout << e.what() << endl;}
}

在这里插入图片描述
这里有了RAII的设计,就不用担心p1p2的内存泄漏问题了。

2.2.智能指针的使用及原理

像上面的SmartPtr还不能称其为智能指针,因为它还不具备指针的行为。因此还需要重载*->,让其像指针一样去使用。

T& operator*()
{return *_ptr;
}T* operator->()
{return _ptr;
}

2.2.1.auto_ptr

C++98库中就提供了auto_ptr智能指针。其实现原理就是将资源管理权进行转移。

// auto_ptr 简单模拟实现
template<class T>
class auto_ptr
{
public:auto_ptr(T* ptr = nullptr): _ptr(ptr){}auto_ptr(auto_ptr<T>& ap): _ptr(ap._ptr){// 资源管理权转移,但同时被转移对象也被悬空ap._ptr = nullptr;}auto_ptr<T>& operator=(auto_ptr<T>& ap){if (this != &ap){// 被赋值对象需要先被清理delete _ptr;_ptr = ap._ptr;ap._ptr = nullptr;}return *this;}~auto_ptr(){delete _ptr;}T& operator*(){return *_ptr;}T* operator->(){return _ptr;}
private:T* _ptr;
};

auto_ptr可以说是一个失败的设计, 所以能不用就不用。

2.2.2.unique_ptr

unique_ptr的实现就是简单粗暴的防拷贝。

// unique_ptr 简单模拟实现
template<class T>
class unique_ptr
{
public:unique_ptr(T* ptr = nullptr): _ptr(ptr){}// 防拷贝unique_ptr(const unique_ptr<T>& up) = delete;unique_ptr<T>& operator=(const unique_ptr<T>& up) = delete;~unique_ptr(){delete _ptr;}T& operator*(){return *_ptr;}T* operator->(){return _ptr;}
private:T* _ptr;
};

2.2.3.shared_ptr

shared_ptr支持拷贝。
shared_ptr是通过引用计数的方式来支持拷贝,支持多个shared_ptr对象共享资源的。
shared_ptr中,为每份资源都维护着一份计数。当一个shared_ptr对象被析构时,引用计数就减一,直到引用计数减为0,才释放资源。

// shared_ptr 简单模拟实现
template<class T>
class shared_ptr
{
public:// 构造 引用计数初始化为1shared_ptr(T* ptr = nullptr): _ptr(ptr), _pCount(new int(1)){}// 拷贝 ++引用计数shared_ptr(const shared_ptr<T>& sp): _ptr(sp._ptr), _pCount(sp._pCount){++(*_pCount);}// 赋值 ++引用计数shared_ptr<T>& operator=(const shared_ptr<T>& sp){if (_ptr != sp._ptr){// 需要先处理好当前资源的释放Release();_ptr = sp._ptr;_pCount = sp._pCount;++(*_pCount);}return *this;}void Release(){if (--(*_pCount) == 0){cout << "void Release()" << endl;delete _ptr;delete _pCount;}}// 直到引用计数减为0才彻底释放资源~shared_ptr(){Release();}T& operator*(){return *_ptr;}T* operator->(){return _ptr;}int use_count(){return *_pCount;}T* get(){return _ptr;}
private:T* _ptr;int* _pCount; // 一个资源,配一个计数,多个智能指针对象共管
};

shared_ptr引用计数不能采用静态计数。静态成员属于整个类,属于类的所有对象。如果申请了两份相同类型的资源,无法做到区分。

2.2.4.shared_ptr的循环引用问题 & weak_ptr

class Node
{
public:int _val;std::shared_ptr<Node> _prev;std::shared_ptr<Node> _next;~Node(){cout << "~Node()" << endl;}
};void Test4()
{std::shared_ptr<Node> n1(new Node);std::shared_ptr<Node> n2(new Node);cout << n1.use_count() << endl;cout << n2.use_count() << endl;n1->_next = n2;n2->_prev = n1;cout << n1.use_count() << endl;cout << n2.use_count() << endl;
}

在这里插入图片描述
上面的程序结束后资源有没有被释放呢?
结果告诉我们是没有的(如果释放了的话会调用析构打印~Node())。
那为什么没有释放呢?
如图,展示了程序中节点的指向情况:
在这里插入图片描述
在Test4()结束的时候,n2先析构,n1再析构,此时左边的节点和右边的节点,它们的引用计数都由2减为1;
此时,由左边节点的_next管着右边的节点,由右边节点的_prev管着左边的节点;
只要左边节点的_next被析构,右边的节点引用计数减为0,就可以被delete;
而左边节点的_next要析构,就必须让左边的节点被delete,左边节点的_next作为成员才会被析构;
而左边的节点要被delete,就必须析构右边节点的_prev,使左边的节点引用计数减为0;
而右边节点的_prev要析构,就必须让右边的节点被delete,右边节点的_prev作为成员才会被析构;
而右边的节点要被delete,就必须析构左边节点的_next,使右边的节点引用计数减为0。

由于左边节点的_next和右边节点的_prev相互牵制着,最后谁也释放不了。这就是循环引用问题。
weak_ptr可以用于解决循环引用问题,而且weak_ptr正是作为辅助型智能指针,为了配合解决shared_ptr的循环引用问题才被设计出来的。

// weak_ptr 的简单模拟实现
template<class T>
class weak_ptr
{
public:weak_ptr(): _ptr(nullptr){}weak_ptr(const shared_ptr<T>& sp): _ptr(sp.get()){}weak_ptr(const weak_ptr<T>& wp): _ptr(wp._ptr){}weak_ptr<T>& operator=(const shared_ptr<T>& sp){_ptr = sp.get();return *this;}weak_ptr<T>& operator=(const weak_ptr<T>& wp){_ptr = wp._ptr;return *this;}T& operator*(){return *_ptr;}T* operator->(){return _ptr;}
private:T* _ptr;
};

weak_ptr不是常规的智能指针,它没有RAII,它可以访问和修改资源,但不参与资源释放管理。
weak_ptr主要是用shared_ptr构造,用来解决shared_ptr的循环引用问题。

class Node
{
public:int _val;std::weak_ptr<Node> _prev;std::weak_ptr<Node> _next;~Node(){cout << "~Node()" << endl;}
};

在这里插入图片描述
智能指针总结
智能指针的设计主要考虑三点:

  1. 利用RAII思想来管理释放资源
  2. 保持有指针一样的行为
  3. 解决拷贝问题

2.3.定制删除器

对于申请的资源需要释放,但对于不同的申请方式,就需要不同的释放方式。定制删除器正是为了使申请与释放的方式进行匹配。

void Test5()
{// shared_ptr 支持构造时传删除器 仿函数版std::shared_ptr<Node> n1(new Node[5], DeleteArray<Node>());std::shared_ptr<Node> n2(new Node, Delete<Node>());std::shared_ptr<int> n3(new int[5], DeleteArray<int>());std::shared_ptr<int> n4(new int, Delete<int>());std::shared_ptr<int> n5((int*)malloc(sizeof 12), Free<int>());// shared_ptr lambda版std::shared_ptr<Node> n6(new Node[5], [](Node* ptr) {cout << "delete[] ptr;" << endl; delete[] ptr; });std::shared_ptr<Node> n7(new Node, [](Node* ptr) {cout << "delete ptr;" << endl; delete ptr; });std::shared_ptr<int> n8(new int[5], [](int* ptr) {cout << "delete[] ptr;" << endl; delete[] ptr; });std::shared_ptr<int> n9(new int, [](int* ptr) {cout << "delete ptr;" << endl; delete ptr; });std::shared_ptr<int> n10((int*)malloc(sizeof 12), [](int* ptr) {cout << "free(ptr);" << endl; free(ptr); });std::shared_ptr<FILE> n11(fopen("test.cpp", "r"), [](FILE* ptr) {cout << "fclose(ptr);" << endl; fclose(ptr); });// unique_ptr 支持类模板传迭代器类型,不支持构造时传迭代器std::unique_ptr<Node, DeleteArray<Node>> n12(new Node[5]);
}

在这里插入图片描述

看到这里,你能回答下列问题了吗?

  1. 为什么需要智能指针?
  2. 什么是RAII
  3. auto_ptr/unique_ptr/shared_ptr/weak_ptr之间的使用区别?
  4. 什么是循环引用?如何解决循环引用?

相关文章:

【C++】异常 智能指针

C异常 & 智能指针 1.C异常1.1.异常的抛出与捕获1.2.异常体系1.3.异常安全与规范1.4.异常优缺点 2.智能指针2.1.RAII2.2.智能指针的使用及原理2.2.1.auto_ptr2.2.2.unique_ptr2.2.3.shared_ptr2.2.4.shared_ptr的循环引用问题 & weak_ptr 2.3.定制删除器 1.C异常 C异常…...

切换数据库的临时表空间为temp1 / 切换数据库的undo表空间为 undotbs01

目录 ​编辑 一、切换临时表空间 1、登录数据库 2、查询默认临时表空间 3、创建临时表空间temp1&#xff08;我们的目标表空间&#xff09; 4、修改默认temp表空间 5、查询用户默认临时表空间 6、命令总结&#xff1a; 二、切换数据库的undo表空间 1、查询默认undo表…...

react: scss使用样式

方式一&#xff1a; 将样式作为模块使用 //List.tsx import styles from /styles/apppublish.module.scss <div className{styles.contentOverflow}></div>//apppublish.module.scss .contentOverflow {height: 100%;overflow-y: auto;display: flex;flex-directi…...

JAVA深化篇_36—— Java网络编程中的常用类

Java网络编程中的常用类 Java为了跨平台&#xff0c;在网络应用通信时是不允许直接调用操作系统接口的&#xff0c;而是由java.net包来提供网络功能。下面我们来介绍几个java.net包中的常用的类。 InetAddress的使用 作用&#xff1a;封装计算机的IP地址和DNS&#xff08;没…...

python操作链接数据库和Mysql中的事务在python的处理

python操作数据库 pymysql模块: pip install pymysql作用:可以实现使用python程序链接mysql数据库&#xff0c;且可以直接在python中执行sql语句 添加操作 import pymysql #1.创建链接对象c conn pymysql.Connect(host127.0.0.1,#数据库服务器主机地址port3306, #mysql的端口…...

【qemu逃逸】XCTF 华为高校挑战赛决赛-pipeline

前言 虚拟机用户名: root 无密码 设备逆向与漏洞分析 程序没有去符合, 还是比较简单. 实例结构体如下: 先总体说一下流程: encode 为 base64 编码函数, decode 为 base64 解码函数. 然后 encPipe 和 decPipe 分别存放编码数据和解码数据, 分别有四个: 其中 EncPipeLine 中…...

muduo源码剖析之TcpClient客户端类

简介 muduo用TcpClient发起连接&#xff0c;TcpClient有一个Connector连接器&#xff0c;TCPClient使用Conneccor发起连接, 连接建立成功后, 用socket创建TcpConnection来管理连接, 每个TcpClient class只管理一个TcpConnecction&#xff0c;连接建立成功后设置相应的回调函数…...

C语言——switch语句判断星期

#define _CRT_SECURE_NO_WARNINGS 1#include<stdio.h> int main() {int day 0;scanf("请输入1-7之间的整数&#xff1a;%d",&day);switch(day){case 1:printf("星期一\n");break;case 2:printf("星期二\n");break;case 3:printf(&quo…...

栈回溯之CmBacktrace

简介 CmBacktrace &#xff08;Cortex Microcontroller Backtrace&#xff09;是一款针对 ARM Cortex-M 系列 MCU 的错误代码自动追踪、定位&#xff0c;错误原因自动分析的开源库。主要特性如下&#xff1a; 支持的错误包括&#xff1a; 断言&#xff08;assert&#xff09;…...

node插件MongoDB(二)——MongoDB的基本命令

文章目录 前言1. 数据库命令&#xff08;1&#xff09;显示所有数据库&#xff08;2&#xff09;切换指定数据库&#xff08;若没有自动创建&#xff09;&#xff08;3&#xff09;显示当前所在数据库&#xff08;4&#xff09;删除当前数据库 2.集合&#xff08;表名&#xff…...

【Git】推送Github失败:remote: Permission to xxx/*.git denied to xxx

在github上&#xff0c;创建了token&#xff0c;推送代码报没权限 #设置token git remote set-url origin <your.token>github.com/<your.name>/hello-git.git#推送代码 #git push -u origin main remote: Permission to xxx/hello-git.git denied to xxx. fatal:…...

Flink -- 状态与容错

1、Stateful Operations 有状态算子&#xff1a; 有状态计算&#xff0c;使用到前面的数据&#xff0c;常见的有状态的算子&#xff1a;例如sum、reduce&#xff0c;因为它们在计算的时候都是用到了前面的计算的结果 总结来说&#xff0c;有状态计算并不是独立存在的&#xf…...

Linux C语言进阶-D15递归函数和函数指针

递归函数 指一个函数的函数体中直接或间接调用了该函数本身 执行过程分为两个过程&#xff1a; 递推过程&#xff1a;从原问题出发&#xff0c;按递归公式递推从未知到已知&#xff0c;最终达到递推终止条件 回归阶段&#xff1a;按递归终止条件求出结果&#xff0c;逆向逐步…...

LeetCode算法心得——全排列(回溯型排列)

大家好&#xff0c;我是晴天学长&#xff0c;排列型的回溯&#xff0c;需要的小伙伴可以关注支持一下哦&#xff01;后续会继续更新的。&#x1f4aa;&#x1f4aa;&#x1f4aa; 1) .全排列 给定一个不含重复数字的数组 nums &#xff0c;返回其 所有可能的全排列 。你可以 按…...

读取W25Q64的设备ID时输出0xff

发现的问题 读取W25Q64的设备ID时输出0xff 找到的不同解决方法 检查MISO和MOSI是否接对。MISO->DO&#xff0c;MOSI->DI检查程序在初始化spi时是否将SS拉高、SCK拉低如果是硬件spi那么检查SPI的初始化函数中&#xff0c;时钟极性SPI_CPOL误选为SPI_CPOL_Low&#xff0…...

【Docker】Docker 网络

引言 Docker是一个开源的应用容器引擎&#xff0c;它允许开发者将应用及其依赖打包到一个可移植的容器中&#xff0c;然后发布到任何流行的Linux机器或Windows机器上&#xff0c;也可以实现虚拟化。Docker的主要优势之一是其网络功能&#xff0c;而网络功能的核心就是网络驱动…...

Flutter学习:使用CustomPaint绘制路径

Flutter学习&#xff1a;认识CustomPaint组件和Paint对象 Flutter学习&#xff1a;使用CustomPaint绘制路径 Flutter学习&#xff1a;使用CustomPaint绘制图形 Flutter学习&#xff1a;使用CustomPaint绘制文字 Flutter学习&#xff1a;使用CustomPaint绘制图片 drawPath 绘制路…...

软件模拟SPI协议的理解和使用编写W25Q64

SPI软件模拟的时序 SPI协议中&#xff0c;NSS、SCK、MOSI由主机产生&#xff0c;MISO由从机产生&#xff0c;在SCK每个时钟周期MOSI、MISO传输一位数据&#xff0c;数据的输入输出是同时进行的&#xff0c;所以读写数据也可以视作交换数据。所以读写时对数据位的控制都是用同一…...

SQLI手动注入和python sqlmap代码注入

sql教程&#xff1a; https://www.w3school.com.cn/sql/index.asp数据库&#xff1a; mysql oracle mssql常用方法 system_user() 系统用户名 user() 用户名 current_user() 当前用户名 session_user() 连接数据库的用户名 d…...

MemcachedRedis构建缓存服务器 (数据持久化,主从同步,哨兵模式)

Memcached/redis是高性能的分布式内存缓存服务器,通过缓存数据库查询结果&#xff0c;减少数据库访问次数&#xff0c;以提高动态Web等应用的速度、 提高可扩展性。降低数据库读的压力 Nsql的优点&#xff1a;高可扩展性&#xff0c;分布式计算&#xff0c;低成本&#xff0c;…...

[特殊字符] 智能合约中的数据是如何在区块链中保持一致的?

&#x1f9e0; 智能合约中的数据是如何在区块链中保持一致的&#xff1f; 为什么所有区块链节点都能得出相同结果&#xff1f;合约调用这么复杂&#xff0c;状态真能保持一致吗&#xff1f;本篇带你从底层视角理解“状态一致性”的真相。 一、智能合约的数据存储在哪里&#xf…...

C++初阶-list的底层

目录 1.std::list实现的所有代码 2.list的简单介绍 2.1实现list的类 2.2_list_iterator的实现 2.2.1_list_iterator实现的原因和好处 2.2.2_list_iterator实现 2.3_list_node的实现 2.3.1. 避免递归的模板依赖 2.3.2. 内存布局一致性 2.3.3. 类型安全的替代方案 2.3.…...

模型参数、模型存储精度、参数与显存

模型参数量衡量单位 M&#xff1a;百万&#xff08;Million&#xff09; B&#xff1a;十亿&#xff08;Billion&#xff09; 1 B 1000 M 1B 1000M 1B1000M 参数存储精度 模型参数是固定的&#xff0c;但是一个参数所表示多少字节不一定&#xff0c;需要看这个参数以什么…...

初学 pytest 记录

安装 pip install pytest用例可以是函数也可以是类中的方法 def test_func():print()class TestAdd: # def __init__(self): 在 pytest 中不可以使用__init__方法 # self.cc 12345 pytest.mark.api def test_str(self):res add(1, 2)assert res 12def test_int(self):r…...

springboot 日志类切面,接口成功记录日志,失败不记录

springboot 日志类切面&#xff0c;接口成功记录日志&#xff0c;失败不记录 自定义一个注解方法 import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target;/***…...

redis和redission的区别

Redis 和 Redisson 是两个密切相关但又本质不同的技术&#xff0c;它们扮演着完全不同的角色&#xff1a; Redis: 内存数据库/数据结构存储 本质&#xff1a; 它是一个开源的、高性能的、基于内存的 键值存储数据库。它也可以将数据持久化到磁盘。 核心功能&#xff1a; 提供丰…...

统计学(第8版)——统计抽样学习笔记(考试用)

一、统计抽样的核心内容与问题 研究内容 从总体中科学抽取样本的方法利用样本数据推断总体特征&#xff08;均值、比率、总量&#xff09;控制抽样误差与非抽样误差 解决的核心问题 在成本约束下&#xff0c;用少量样本准确推断总体特征量化估计结果的可靠性&#xff08;置…...

__VUE_PROD_HYDRATION_MISMATCH_DETAILS__ is not explicitly defined.

这个警告表明您在使用Vue的esm-bundler构建版本时&#xff0c;未明确定义编译时特性标志。以下是详细解释和解决方案&#xff1a; ‌问题原因‌&#xff1a; 该标志是Vue 3.4引入的编译时特性标志&#xff0c;用于控制生产环境下SSR水合不匹配错误的详细报告1使用esm-bundler…...

ffmpeg(三):处理原始数据命令

FFmpeg 可以直接处理原始音频和视频数据&#xff08;Raw PCM、YUV 等&#xff09;&#xff0c;常见场景包括&#xff1a; 将原始 YUV 图像编码为 H.264 视频将 PCM 音频编码为 AAC 或 MP3对原始音视频数据进行封装&#xff08;如封装为 MP4、TS&#xff09; 处理原始 YUV 视频…...

RFID推动新能源汽车零部件生产系统管理应用案例

RFID推动新能源汽车零部件生产系统管理应用案例 一、项目背景 新能源汽车零部件场景 在新能源汽车零部件生产领域&#xff0c;电子冷却水泵等关键部件的装配溯源需求日益增长。传统 RFID 溯源方案采用 “网关 RFID 读写头” 模式&#xff0c;存在单点位单独头溯源、网关布线…...