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版本-切换版本 再新建一个网站 点击设置 添加反向代理-代理名称随便…...
多场景 OkHttpClient 管理器 - Android 网络通信解决方案
下面是一个完整的 Android 实现,展示如何创建和管理多个 OkHttpClient 实例,分别用于长连接、普通 HTTP 请求和文件下载场景。 <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas…...
STM32+rt-thread判断是否联网
一、根据NETDEV_FLAG_INTERNET_UP位判断 static bool is_conncected(void) {struct netdev *dev RT_NULL;dev netdev_get_first_by_flags(NETDEV_FLAG_INTERNET_UP);if (dev RT_NULL){printf("wait netdev internet up...");return false;}else{printf("loc…...
Linux相关概念和易错知识点(42)(TCP的连接管理、可靠性、面临复杂网络的处理)
目录 1.TCP的连接管理机制(1)三次握手①握手过程②对握手过程的理解 (2)四次挥手(3)握手和挥手的触发(4)状态切换①挥手过程中状态的切换②握手过程中状态的切换 2.TCP的可靠性&…...
最新SpringBoot+SpringCloud+Nacos微服务框架分享
文章目录 前言一、服务规划二、架构核心1.cloud的pom2.gateway的异常handler3.gateway的filter4、admin的pom5、admin的登录核心 三、code-helper分享总结 前言 最近有个活蛮赶的,根据Excel列的需求预估的工时直接打骨折,不要问我为什么,主要…...
Python实现prophet 理论及参数优化
文章目录 Prophet理论及模型参数介绍Python代码完整实现prophet 添加外部数据进行模型优化 之前初步学习prophet的时候,写过一篇简单实现,后期随着对该模型的深入研究,本次记录涉及到prophet 的公式以及参数调优,从公式可以更直观…...
第一篇:Agent2Agent (A2A) 协议——协作式人工智能的黎明
AI 领域的快速发展正在催生一个新时代,智能代理(agents)不再是孤立的个体,而是能够像一个数字团队一样协作。然而,当前 AI 生态系统的碎片化阻碍了这一愿景的实现,导致了“AI 巴别塔问题”——不同代理之间…...
【Zephyr 系列 10】实战项目:打造一个蓝牙传感器终端 + 网关系统(完整架构与全栈实现)
🧠关键词:Zephyr、BLE、终端、网关、广播、连接、传感器、数据采集、低功耗、系统集成 📌目标读者:希望基于 Zephyr 构建 BLE 系统架构、实现终端与网关协作、具备产品交付能力的开发者 📊篇幅字数:约 5200 字 ✨ 项目总览 在物联网实际项目中,**“终端 + 网关”**是…...
前端开发面试题总结-JavaScript篇(一)
文章目录 JavaScript高频问答一、作用域与闭包1.什么是闭包(Closure)?闭包有什么应用场景和潜在问题?2.解释 JavaScript 的作用域链(Scope Chain) 二、原型与继承3.原型链是什么?如何实现继承&a…...
uniapp中使用aixos 报错
问题: 在uniapp中使用aixos,运行后报如下错误: AxiosError: There is no suitable adapter to dispatch the request since : - adapter xhr is not supported by the environment - adapter http is not available in the build 解决方案&…...
多模态大语言模型arxiv论文略读(108)
CROME: Cross-Modal Adapters for Efficient Multimodal LLM ➡️ 论文标题:CROME: Cross-Modal Adapters for Efficient Multimodal LLM ➡️ 论文作者:Sayna Ebrahimi, Sercan O. Arik, Tejas Nama, Tomas Pfister ➡️ 研究机构: Google Cloud AI Re…...
