C++11 智能指针
目录
智能指针
异常导致执行流乱跳
智能指针解决问题
auto_ptr
unique_ptr
weak_ptr
智能指针
由于C++11引入异常之后,执行流乱跳,所以导致之前 malloc/new 的空间很容易没有被释放,导致内存泄露问题。
所以这时候,我们就需要一个可以自动释放的对象来管理这些空间,来让这些空间都能被释放,不会发生内存泄露。
下面我们先看下异常如何会引起执行流乱跳的问题。
异常导致执行流乱跳
我们看下面一段代码,下面我们在 main 函数中中调用一个 test 函数, test 函数中调用 new 开空间等,还调用了 div 函数,div 函数里面如果发现有 0 ,那么就抛出除0错误。
class A
{
public:A(int a = 0):_a(a){cout << "构造函数:A(int a = 0)" << endl;}
~A(){cout << "析构函数:~A()" << endl;}int get_a(){return _a;}private:int _a;
};
int Div(int a, int b)
{if (b == 0){throw invalid_argument("除0错误");}return a / b;
}
void test1()
{A* APtr = new A;int a = 0, b = 0;cont << "输入:";cin >> a >> b;Div(a, b);
delete APtr;
}
int main()
{try{test1();}catch (exception& e){cout << e.what() << endl;}return 0;
}
-
这个test函数里面,我们在 delete APtr 之前调用 div 函数。
-
如果有除0错误,那么就会直接跳到 main 函数里面,不会进行释放 A 类型的指针,所以就发生了内存泄露问题。
结果:
构造函数:A(int a = 0)
输入:1 0
除0错误
-
这里并没有调用析构函数!!!
如果没有除0的话,那么还是会正常释放的:
构造函数:A(int a = 0)
输入:1 1
析构函数:~A()
-
所以如果是我们自己 mallo/new 的指针的话,那么还是容易导致内存泄露。
-
那么怎么解决呢?我们可以在 test 里面捕获一下异常,然后再里面进行释放内存,然后再把错误抛出去!
void test1()
{A* APtr = new A;cout << "输入:";int a = 0, b = 0;cin >> a >> b;try{Div(a, b);}catch (...){delete APtr;// 释放throw;}delete APtr;
}
-
如果是这样子的话,那么就可以解决了。
结果:
构造函数:A(int a = 0)
输入:1 0
析构函数:~A()
除0错误
但是上面这种情况还是比较简单的情况,那么如果复杂一点呢?
void test2()
{A* APtr1 = new A(10); // 这里可能会抛异常A* APtr2 = new A(20); // 这里可能会抛异常
div(1, 0); // 这里可能会抛异常
delete APtr1;delete APtr2;
}
-
这里三处可能会抛异常,那么怎么办呢?
-
如果是第一处抛异常,那么还好,没有内存泄露。
-
如果是第二处抛异常,那么我们还需要释放第一处开的空间。
-
如果是第三处抛异常,我们又需要释放第一处和第二处开的空间,而且如果又更多的动态内存的申请呢?
-
所以这样就很容易导致程序出现问题!
-
那么我们可以怎么解决?
智能指针解决问题
-
首先我们先说一下什么是智能指针。
-
当我们需要对 malloc/new 的空间进行管理时,我们可不可以让一个对象去管理这段空间。
-
当这个对象构造时,我们把空间交给这个对象。
-
当该对象析构时,然后让该对象释放掉这段空间。
-
所以指针指针也是一个对象。
auto_ptr
auto_ptr 是一个智能指针,我们下面先看一下如何实现,可以让该对象释放时可以释放动态开辟的空间。
首先我们可以再该对象的构造函数里面用一个指针去构造,而该对象也就是管理的这段空间:
template<class T>class auto_ptr{public:// 构造函数 用一个指针初始化auto_ptr(T* ptr = nullptr):_ptr(ptr){}// 析构函数 释放掉管理的空间~auto_ptr(){if (_ptr){delete _ptr;}}private:T* _ptr;};
那么我们使用 auto_ptr 看一下是否会又内存泄露:
void test3()
{lxy::auto_ptr<A> Aptr1 = new A(10);lxy::auto_ptr<A> Aptr2 = new A(20);
Div(1, 0);// 除0错误,会抛异常
}
结果:
构造函数:A(int a = 0)
构造函数:A(int a = 0)
析构函数:~A()
析构函数:~A()
除0错误
-
这里看到并没有内存泄露。
-
而且其实这里我们也并没有手动的释放开辟的空间,我们只是让 auto_ptr 进行管理了。
-
但是如果我们想要访问A对象里面的内容呢?
-
所以我们的智能指针还需要做到像指针一样。
-
也就是重载 * 和 ->
T& operator*(){return *_ptr;}T* operator->(){return _ptr;}
测试代码:
void test3()
{lxy::auto_ptr<A> Aptr1 = new A(10);lxy::auto_ptr<A> Aptr2 = new A(20);cout << Aptr1->get_a() << endl;cout << (*Aptr2).get_a() << endl;
}
结果:
构造函数:A(int a = 0)
构造函数:A(int a = 0)
10
20
析构函数:~A()
析构函数:~A()
这里就是没问题的,那么如果我们想拷贝一下呢?
我们试一下:
void test4()
{lxy::auto_ptr<A> Aptr1 = new A(10);lxy::auto_ptr<A> Aptr2(Aptr1);
}
-
上面这段代码,用 Aptr1 拷贝 Aptr2 这里是浅拷贝,所以auto_ptr 里面的成员变量是指针类型,发生值拷贝
-
所以此时的Aptr1 和 Aptr2 里面的指针指向同一块空间。
-
然后等出作用域的时候,两个对象都会释放,然后就会导致一块空间被释放多次。
结果:
构造函数:A(int a = 0)
析构函数:~A()
析构函数:~A()
-
上面就奔溃掉了~
那么怎么解决呢?我们可以采用管理资源转移的方法来实现!
也就是当我们发生拷贝时,我们就可以让管理的内容从a对象转移到b对象!
~auto_ptr(){if (_ptr){delete _ptr;}}
这里我们再看上面的那一段代码就没问题了:
构造函数:A(int a = 0)
析构函数:~A()
-
正常释放。
-
但是当我们引入资源转移之后,其他的问题又来了,就是悬空!
-
当我们现在再使用 Aptr1 对象访问里面的内容时,就会报错!
-
所以有人使用这个的话,那么又是一个严重的问题。
// 这里为了方便测试,我们将 A 类的成员变量公有了...
void test4()
{lxy::auto_ptr<A> Aptr1 = new A(10);lxy::auto_ptr<A> Aptr2(Aptr1);(*Aptr1)._a++;(*Aptr2)._a++;
}
结果:
构造函数:A(int a = 0)
-
上面对空指针解引用了,会直接导致进程奔溃掉。
那么这个问题如何解决呢?
我们看下面的其他类型的智能指针!
unique_ptr
-
该智能指针解决上面指针悬空的方法就是直接不允许拷贝。
template<class T>class unique_ptr{public:unique_ptr(T* ptr = nullptr):_ptr(ptr){}~unique_ptr(){if (_ptr){delete _ptr;}}T& operator*(){return *_ptr;}T* operator->(){return _ptr;}private:unique_ptr(unique_ptr& ptr) {};unique_ptr& operator=(unique_ptr& ptr) {};private:T* _ptr;};
-
上面实现不允许拷贝的方法就是将拷贝构造和赋值重载都只声明不实现!
-
但是只声明不实现,可能会有人再类外面实现,所以为了避免这样做,可以声明为私有。
-
其实还有一种解决方法,那么就是关键字 delete
-
delete 表示删除掉该函数。
unique_ptr(unique_ptr& ptr) = delete;unique_ptr& operator=(unique_ptr& ptr) = delete;
-
这就是将一个函数删除!
-
删除掉的函数,就不能被调用。
下面我们测试一下:
void test5()
{lxy::unique_ptr<A> Aptr1 = new A(10);lxy::unique_ptr<A> Aptr2(Aptr1);
}
这里是不能调用的,我们可以看一下报错信息:
“lxy::unique_ptr<A>::unique_ptr(lxy::unique_ptr<A> &)”: 尝试引用已删除的函数
但是这里还是有问题,那么如果我们需要拷贝呢?
所以仅仅是这和 unique_ptr 还是不够的。
我们还是需要可以拷贝的智能指针,我们来看下面!
sharded_ptr
-
shared_ptr 是可以拷贝的,那么如何实现可以拷贝呢?
-
如果只是单纯的拷贝过去,那么就是值拷贝,可能会导致多次释放。
-
那么我们可以使用引用计数的方法,来表示该空间一共被多少个对象所管理。
-
那么这时候我们又出现问题了,我们怎么实现引用计数呢?
template<class T>class shared_ptr{public:private:T* _ptr;int _count;};
-
上面这样实现可以吗?
-
不可以!
-
因为如果是成员变量的话,那么 count 是每一个对象都又一个的,那么就是无法起到引用计数的作用。
-
我们需要的是可以多个对象来管理一块空间的引用计数,而不是每一个对象都有一个引用计数!
-
那么我们怎么办呢?
-
我们再看下面的这种方法!
template<class T>class shared_ptr{public:private:T* _ptr;static int _count;};
-
我们使用 static 的变量可以吗?
-
如果我们使用 static 的变量,那么该变量是属于整个类的。
-
同时也是属于该类的所有成员的,那么如果现在有两个管理不同空间的对象,那么此时的引用计数应该是多少呢?
-
所以此时也是不可以的!
-
那么我们应该怎么办?
-
我们可以让引用计数是一个动态的,也就是引用计数可以也是一个指针,让管理相同空间的对象的引用计数也是相同的!
template<class T>class shared_ptr{public:private:T* _ptr;int* _pcount;};
-
所以此时,我们只需要再构造函数的时候对这两个变量初始化就可以了。
构造函数
shared_ptr(T* ptr = nullptr):_ptr(ptr), _pcount(new int(1)){}
析构函数
~shared_ptr(){if (--(*_pcount) == 0){delete _ptr;delete _pcount;}}
运算符重载
T& operator*(){return *_ptr;}T* operator->(){return _ptr;}
拷贝构造
拷贝构造就是将成员变量的两个指针拷贝给其他的类。
拷贝结束后,对引用计数进行加加。
shared_ptr(shared_ptr& ptr):_ptr(ptr._ptr),_pcount(ptr._pcount){++(*_pcount);}
赋值重载
赋值重载这里需要考虑一些问题:
-
自己给自己赋值怎么办?
-
被拷贝对象应该怎么办
-
拷贝对象应该怎么办?
shared_ptr& operator=(shared_ptr& ptr){// 判断是否是自己给自己赋值if (_ptr == ptr._ptr)return *this;// 判断是否需要销毁空间if (--(*_pcount) == 0){delete _ptr;delete _pcount;}_ptr = ptr._ptr;_pcount = ptr._pcount;++(*_pcount);}
测试代码:
void test6()
{lxy::shared_ptr<A> Aptr1(new A(20));lxy::shared_ptr<A> Aptr2(Aptr1);lxy::shared_ptr<A> Aptr3;Aptr3 = Aptr2;lxy::shared_ptr<A> Aptr4(new A(100));lxy::shared_ptr<A> Aptr5(Aptr4);
}
结果:
构造函数:A(int a = 0)
构造函数:A(int a = 0)
析构函数:~A()
析构函数:~A()
-
上面这里无论是拷贝还是赋值都没有问题。
-
实际上 shared_ptr 再一般情况下都是没有问题的,但是再特殊情况下还是会有问题!
我们来看下面的代码:
struct Node
{A _a;lxy::shared_ptr<Node> next;lxy::shared_ptr<Node> prev;
};
void test7()
{lxy::shared_ptr<Node> n1(new Node);lxy::shared_ptr<Node> n2(new Node);
}
首先我们看 test7 函数,这样是没有问题的:
构造函数:A(int a = 0)
构造函数:A(int a = 0)
析构函数:~A()
析构函数:~A()
-
这里我们是可以正确的释放的。
-
我们下面让 n1 的 next 指向 n2, n2 的 prev 指向 n1。
void test7()
{lxy::shared_ptr<Node> n1(new Node);lxy::shared_ptr<Node> n2(new Node);n1->next = n2;n2->prev = n1;
}
结果:
构造函数:A(int a = 0)
构造函数:A(int a = 0)
-
这里我们只调用到了构造函数,并没有析构为什么?
-
下面我们只让 n1 的 next 指向 n2试一下,看有没有问题!
void test7()
{lxy::shared_ptr<Node> n1(new Node);lxy::shared_ptr<Node> n2(new Node);n1->next = n2;
}
结果:
构造函数:A(int a = 0)
构造函数:A(int a = 0)
析构函数:~A()
析构函数:~A()
-
这里看到只让 n1 的 next 指向 n2 是没有问题的,那么下面让 n2 的 prev 指向 n1 看有没有问题:
void test7()
{lxy::shared_ptr<Node> n1(new Node);lxy::shared_ptr<Node> n2(new Node);n2->prev = n1;
}
结果:
构造函数:A(int a = 0)
构造函数:A(int a = 0)
析构函数:~A()
析构函数:~A()
-
这里看到,我们让 n2 的 prev 指向 n1 也是没有问题的。
-
说明如果我们只是单个指向的话,是不会出现刚才的内存泄露的问题的。
-
如果有互相指向,那么就是会出现问题!
-
下面分析一下为什么?
我们来分析一下互相指向的话释放是怎么释放的:
struct Node
{A _a;lxy::shared_ptr<Node> next;lxy::shared_ptr<Node> prev;
};lxy::shared_ptr<Node> n1(new Node);
lxy::shared_ptr<Node> n2(new Node);
-
n1 什么时候释放?n1 这个节点由 n1 和 n2 的prev 共同管理,所以如果 n1 释放的话,那么就需要 n2 的 prev 释放了才释放。
-
那么n2 的 prev 什么时候释放?当 n2 释放了才释放。
-
n2 什么时候释放?n2 和 n1 的next 再共同管理着一块空间,那么 n2 释放就需要 n1 的next 释放了才释放。
-
那么 n1 的 next 什么时候释放?当 n1 释放了才释放。
这发现是一个循环一样的东西,首先我们是 n1 什么时候释放,而最后也牵扯到了当 n1 释放的时候才释放。
所以此时就是一个循环,那么怎么解决呢?
那么我们可以让 next 和 prev 不参与管理这块空间不久好了吗?
所以我们可以写一个 weak_ptr,该对象不参与管理,只是为了解决这个问题。
weak_ptr
-
那么该对象如何实现呢?
-
首先我们需要让 weak_ptr 的类型可以指向 shared_ptr 的类型,所以我们需要一个 shared_ptr 的构造函数。
-
那么既然 weak_ptr 不参与管理,所以当 shared_ptr 构造 weak_ptr 后,引用计数也不能加加。
template<class T>class weak_ptr{public:weak_ptr(T* ptr = nullptr):_ptr(ptr){}weak_ptr(shared_ptr<T>& ptr):_ptr(ptr._ptr){}T& operator*(){return *_ptr;}T* operator->(){return _ptr;}private:T* _ptr;};
-
但是这里是不正确的,因为再 shared_ptr 类型的 构造函数,但是那里访问了 shared_ptr 的私有,所以这里会报错!
-
这里可以有两个解决方法:
-
将 weak_ptr 设置为 shared_ptr 的友元。
-
为 shared_ptr 提供一个 get 函数。
-
下面我们采用第二种。
-
// shared_ptr::getPtr()T* getPtr(){return _ptr;}// weak_ptr::weak_ptr(shared_ptr<T>& ptr)weak_ptr(shared_ptr<T>& ptr):_ptr(ptr.getPtr()){}
这样就可以解决问题了,下面我们再看一下那个测试的结果。
struct Node
{A _a;lxy::weak_ptr<Node> next;lxy::weak_ptr<Node> prev;
};void test7()
{lxy::shared_ptr<Node> n1(new Node);lxy::shared_ptr<Node> n2(new Node);n1->next = n2;n2->prev = n1;
}
结果:
构造函数:A(int a = 0)
构造函数:A(int a = 0)
析构函数:~A()
析构函数:~A()
-
这时候就解决问题了。
相关文章:
C++11 智能指针
目录 智能指针 异常导致执行流乱跳 智能指针解决问题 auto_ptr unique_ptr sharded_ptr weak_ptr 智能指针 由于C11引入异常之后,执行流乱跳,所以导致之前 malloc/new 的空间很容易没有被释放,导致内存泄露问题。 所以这时候&#x…...

二、WebGPU阶段间变量(inter-stage variables)
二、WebGPU阶段间变量(inter-stage variables) 在上一篇文章中,我们介绍了一些关于WebGPU的基础知识。在本文中,我们将介绍阶段变量(inter-stage variables)的基础知识。 阶段变量在顶点着色器和片段着色…...
【Linux】31个普通信号
文章目录 1.每种信号的含义2.两种不能被忽略的信号3.两种不能被捕捉的信号 1.每种信号的含义 信号编号信号名信号含义1SIGHUP如果终端接口检测到一个连接断开,则会将此信号发送给与该终端相关的控制进程,该信号的默认处理动作是终止进程。2SIGINT当用户…...

Mac电脑交互式原型设计 Axure RP 8汉化最新 for mac
Axure RP 8是一款专业且快速的原型设计工具,主要用于定义需求、规格、设计功能和界面。这款工具主要适用于用户体验设计师、交互设计师、业务分析师、信息架构师、可用性专家和产品经理等职业。 Axure RP 8的主要特性包括能够快速设计出应用软件或Web网站的线框图、…...

在线免费无时长限制录屏工具 - 录猎在线版
需要录屏的小伙伴注意啦,想要长时间录制又不想花钱的,可以看下这款在线版录屏软件 —— 录猎在线版,一个录屏软件所需要的基本功能它都有,设置录制范围、录制的声音来源、摄像头也能录制的。同时它是支持Windows和Mac系统的&#…...

c语言文件操作详解:fgetc,fputc,fgets,fputs,fscanf,,fprintf,fread,fwrite的使用和区别
前言:在对于c语言的学习中,我们为了持续使用一些数据,为了让我们的数据可以在程序退出后仍然保存并且可以使用,我们引入了文件的概念和操作,本文旨在为大家分享在文件操作中常用的输入输出函数的使用方式和技巧&#x…...
Harmony装饰器
1、装饰器 装饰器是用于装饰类、结构、方法以及变量,并赋予其特殊的含义。如: Component表示自定义组件Entry表示该自定义组件为入口组件State表示组件中的状态变量,状态变量变化会触发UI刷新。 2 、语法范式 Builder/BuilderParam&#…...

如何加快Chrome谷歌浏览器下载速度?
用Chrome打开chrome://flags/...

使用kubectl连接远程Kubernetes(k8s)集群
使用kubectl连接远程Kubernetes集群 环境准备下载kubectl下载地址 安装kubectl并处理配置文件Windows的安装配置安装kubectl拉取配置文件安装kubectl拉取配置文件kubectl命令自动补全 Linux的安装配置安装kubectl拉取配置文件kubectl命令自动补全 环境准备 你需要准备一个Kube…...

Kubernetes革命:云原生时代的应用编排和自动化
文章目录 什么是Kubernetes以及为何它备受欢迎?云原生应用和K8s的关系Kubernetes的核心概念:Pods、Services、ReplicaSets等部署、扩展和管理应用程序的自动化容器编排的演进:Docker到Kubernetes实际用例:企业如何受益于K8s的应用…...
mysql.mongoDb,neo4j数据库对比
#Mysql与MongoDb和Neo4j的一些对比 主要区别 MySQL: 1.MySQL是一种关系型数据库管理系统(RDBMS),广泛用于处理结构化数据。 2.它支持SQL语言,具备成熟的事务处理和数据一致性能力。 3.MySQL适用于大多数传统的基于表…...

unity使用UniStorm 5.1.0.unitypackage增加天气
添加天天气组件unistorm 然后添加一个player 导入包会报错,需要修改代码 using UnityEngine; using UnityEngine.PostProcessing;namespace UnityEditor.PostProcessing {[CustomPropertyDrawer(typeof(UnityEngine.PostProcessing.MinAttribute))]sealed class MinDrawer : …...

Flink实现kafka到kafka、kafka到doris的精准一次消费
1 流程图 2 Flink来源表建模 --来源-城市topic CREATE TABLE NJ_QL_JC_SSJC_SOURCE ( record string ) WITH (connector kafka,topic QL_JC_SSJC,properties.bootstrap.servers 172.*.*.*:9092,properties.group.id QL_JC_SSJC_NJ_QL_JC_SSJC_SOURCE,scan.startup.mode …...

Outlook屏蔽Jira AI提醒
前言:最近不知道为什么jira上的ai小助手抽风,一周发个几千封邮件…导致我现在都不想在邮箱里面跟找垃圾一样找消息了。实在忍无可忍,决定屏蔽AI小助手,方法很简单,follow me~~ 第一步:双击打开电脑版Outloo…...

毛玻璃 has 选择器卡片悬停效果
效果展示 页面结构 从上述的效果展示可以看到,页面是由多个卡片组成,并且鼠标悬停在卡片上时,会旋转用户图片并且韩式对应的用户信息框。 CSS3 知识点 :has 属性的运用 实现页面整体结构 <div class"container"><div…...
[hive]解决group by 字段超过系统规定64个
用开窗函数即可 ( row_number() over(partition by col1,...,col70 oder by xx) rn ) where rn1...
生成老年人的声音sox
sox laoren1.wav laoren2.wav pitch -300...

DC2DC电源设计注意事项--1,Feedback
电源采集图如下图 Feedback 采集电压点应该在靠近负载侧。这样可以减少大电流导线导致的电压差,真实反应输出电压值 FB_1P21采集电路靠近芯片侧, 2.1,采集分压电路上侧为Vout Vnoise, 那么一分压就噪声就小了。假如采集电路远离芯片侧&…...

计算机视觉处理的开源框架
计算机视觉是一门涉及图像和视频分析的领域,有许多开源的框架和库可用于构建计算机视觉应用程序。以下是一些常见的计算机视觉开源框架及其特点,希望对大家有所帮助。北京木奇移动技术有限公司,专业的软件外包开发公司,欢迎交流合…...

最新AI智能创作系统源码AI绘画系统/支持GPT联网提问/支持Prompt应用
AI绘图专业设计 不得将程序用作任何违法违纪内容,不要让亲人两行泪 界面部分图解构: 前台show: 前端部署: 安装pm2管理器 点击设置 选择v16.19.1版本-切换版本 再新建一个网站 点击设置 添加反向代理-代理名称随便…...

Docker 离线安装指南
参考文章 1、确认操作系统类型及内核版本 Docker依赖于Linux内核的一些特性,不同版本的Docker对内核版本有不同要求。例如,Docker 17.06及之后的版本通常需要Linux内核3.10及以上版本,Docker17.09及更高版本对应Linux内核4.9.x及更高版本。…...
Java 语言特性(面试系列2)
一、SQL 基础 1. 复杂查询 (1)连接查询(JOIN) 内连接(INNER JOIN):返回两表匹配的记录。 SELECT e.name, d.dept_name FROM employees e INNER JOIN departments d ON e.dept_id d.dept_id; 左…...

Vue2 第一节_Vue2上手_插值表达式{{}}_访问数据和修改数据_Vue开发者工具
文章目录 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染2. 插值表达式{{}}3. 访问数据和修改数据4. vue响应式5. Vue开发者工具--方便调试 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染 准备容器引包创建Vue实例 new Vue()指定配置项 ->渲染数据 准备一个容器,例如: …...

(二)原型模式
原型的功能是将一个已经存在的对象作为源目标,其余对象都是通过这个源目标创建。发挥复制的作用就是原型模式的核心思想。 一、源型模式的定义 原型模式是指第二次创建对象可以通过复制已经存在的原型对象来实现,忽略对象创建过程中的其它细节。 📌 核心特点: 避免重复初…...
Java线上CPU飙高问题排查全指南
一、引言 在Java应用的线上运行环境中,CPU飙高是一个常见且棘手的性能问题。当系统出现CPU飙高时,通常会导致应用响应缓慢,甚至服务不可用,严重影响用户体验和业务运行。因此,掌握一套科学有效的CPU飙高问题排查方法&…...

脑机新手指南(七):OpenBCI_GUI:从环境搭建到数据可视化(上)
一、OpenBCI_GUI 项目概述 (一)项目背景与目标 OpenBCI 是一个开源的脑电信号采集硬件平台,其配套的 OpenBCI_GUI 则是专为该硬件设计的图形化界面工具。对于研究人员、开发者和学生而言,首次接触 OpenBCI 设备时,往…...

论文阅读:LLM4Drive: A Survey of Large Language Models for Autonomous Driving
地址:LLM4Drive: A Survey of Large Language Models for Autonomous Driving 摘要翻译 自动驾驶技术作为推动交通和城市出行变革的催化剂,正从基于规则的系统向数据驱动策略转变。传统的模块化系统受限于级联模块间的累积误差和缺乏灵活性的预设规则。…...

【Linux手册】探秘系统世界:从用户交互到硬件底层的全链路工作之旅
目录 前言 操作系统与驱动程序 是什么,为什么 怎么做 system call 用户操作接口 总结 前言 日常生活中,我们在使用电子设备时,我们所输入执行的每一条指令最终大多都会作用到硬件上,比如下载一款软件最终会下载到硬盘上&am…...
Pydantic + Function Calling的结合
1、Pydantic Pydantic 是一个 Python 库,用于数据验证和设置管理,通过 Python 类型注解强制执行数据类型。它广泛用于 API 开发(如 FastAPI)、配置管理和数据解析,核心功能包括: 数据验证:通过…...
Java 与 MySQL 性能优化:MySQL 慢 SQL 诊断与分析方法详解
文章目录 一、开启慢查询日志,定位耗时SQL1.1 查看慢查询日志是否开启1.2 临时开启慢查询日志1.3 永久开启慢查询日志1.4 分析慢查询日志 二、使用EXPLAIN分析SQL执行计划2.1 EXPLAIN的基本使用2.2 EXPLAIN分析案例2.3 根据EXPLAIN结果优化SQL 三、使用SHOW PROFILE…...