《C++入门篇》——弥补C不足

文章目录

    • 前言
      • 一.命名空间
      • 二.缺省参数
      • 三.函数重载
      • 四.引用
        • 4.1引用做参数
        • 4.2引用做返回值
      • 五.内联函数
      • 六.小语法
        • 6.1auto
        • 6.2范围for
        • 6.3空指针

前言

C++是业内一门久负盛名的计算机语言,从C语言发展起来的它,不仅支持C语言的语法,还新添加了面向对象、泛型等特性,以及祖师爷本贾尼博士补充C语言的不足。

这一篇我们先来讲讲C++对C语言不足部分的补充。

一.命名空间

在这里插入图片描述

Cpp能很好的支持C,所以在Cpp文件中写C语言程序是没问题的。

从这段代码里,我们好像看不出什么问题,唯一觉得奇怪的是,为什么整型变量要取名为rand这么奇怪。我们接着往下看:

在这里插入图片描述

将变量rand放到全局中,报错信息给出rand重定义,我们想起C语言库里定义有一个叫rand函数与我们定义的rand变量命名冲突了,于是报出了这个语法错误。

那么上面为什么放在局部中没有报错呢?这是因为局部和全局都有时,局部优先!

到这里读者可能会说,在日常写代码中,自己写的又不一定会和自己命名冲突,那么在一个大工程里,数十几个程序员的代码合并到一起,会不会冲突?

在这里插入图片描述

使用命名空间,将其隔离起来,这样就不冲突了,打印rand时,找的是库里的rand函数。

在这里插入图片描述

使用域作用限定符::,可以让编译器在找rand时,先找域作用限定符指定的域里先去找。

#include <stdio.h>
namespace name
{//可以嵌套使用,如果一个命名空间内也有命名冲突,可以再隔离。namespace name1{int a = 0;}namespace name2{int a = 1;}int Add(int x, int y){return x+y;}struct Node{int val;struct Node* next;};
}int main()
{printf("%d\n", name::name1::a);//到name里找name1,name1里找aprintf("%p\n", name::Add);struct name::Node node = {0};//::要加在Node前面return 0;
}

关于namespace关键字,基本的用法和作用讲完了,还有一个较为重要分文件写声明和定义,接着往下看:

//Stack.h文件
#include <assert.h>
namespace sjr
{typedef struct Stack{int* a;int top;int capacity;}Stack;void StackInit(Stack* ps);void StackPush(Stack* ps, int x);
}//Stack.cpp文件
#include "Stack.h"
namespace sjr
{void StackInit(Stack* ps){assert(ps);ps->a = NULL;ps->top = 0;ps->capacity = 0;}void StackPush(Stack* ps, int x){//...}
}

将两个不同文件的声明和定义使用同名的命名空间包起来,就可以实现声明和定义分离的同时都在命名空间内。

编译器会将不同文件的同名命名空间合并在一起,一个文件里有同名的命名空间也会合并,只是我们一般不会这么写。

总结:命名空间是用来弥补C语言命名冲突的不足。有如何创建命名空间、命名空间里的变量、函数、类型都可以正常创建、可以嵌套、声明和定义分离使用同一个命名空间,编译器会合并它们。

域作用限定符是一种指定命名空间里找的方法,以上面栈为例子,我们试着创建栈并插入几个数据:

#include "Stack.h"/*int main
{name::Stack st;name::StackInit(&st);name::StackPush(&st, 1);name::StackPush(&st, 2);name::StackPush(&st, 3);name::StackPush(&st, 4);//每次使用都需要指定,日常练习这样完全没必要
}*///展开命名空间
using namespace name;
int main
{Stack st;StackInit(&st);StackPush(&st, 1);StackPush(&st, 2);StackPush(&st, 3);StackPush(&st, 4);
}

展开命名空间就可以直接使用里面的变量、函数等,但是这和头文件的展开不同,它只是将隔离拆除了,编译器会到展开的命名空间里面去找。如果展开命名空间里的定义与全局的有命名冲突,这样还是会报错!

在项目中全展开不是很好的做法,还有一种指定展开的方法:

//第一个C++代码:cout是输出,使用流插入<<,自动识别类型;endl是换行 end line;
#include <iostream>using namespace std;//展开std(std是C++官方库的命名空间)
int main()
{	//cout 和 endl都在命名空间里cout << "hello world"<< endl;return 0;
}
#include <iostream>
//为了cout和endl展开整个库太坑了
using std::cout;
using std::endl;
int main()
{int a = 0;cout << "hello world"<< endl;//cin也在std里,没有指定展开,需要加std::std::cin >> a;//cin是输入,使用流提取>>,自动识别类型。 return 0;
}

总结:编译器默认不会到命名空间里找,这是解决命名冲突的基础。使用::可以单次到指定空间里找、还有using namespace std;展开命名空间,此时里面定义的所有东西都将暴露出来、而using std::cout;则只暴露std里面的cout。

二.缺省参数

在这里插入图片描述

在形参的后面加上一个值,就是缺省参数。Func函数里的a就是缺省参数,在调用Func函数时,如果没有传参,缺省值10将会默认赋值给a打印,否则按实际传递的值打印。

接下来我们看缺省参数更多的知识:

在这里插入图片描述

在这里插入图片描述

以上是全缺省的细节,也就是缺省值要从左往右给

在这里插入图片描述

半缺省遵循缺省参数从右往左给。

这是因为,如果不遵循这个规则,对于传参有很大的歧义。比如Fun2(10);,此时这个10是传给a还是传给b是不确定的;不支持跳跃传参Fun2(,10);,总之对于半缺省,遵循以上规则。

缺省有什么用?请看以下代码:

//Stack.h文件
#include <assert.h>
namespace sjr
{typedef struct Stack{int* a;int top;int capacity;}Stack;//声明和定义分离,缺省写在声明里!void StackInit(Stack* ps, int n = 4);void StackPush(Stack* ps, int x);
}//Stack.cpp文件
#include "Stack.h"
namespace sjr
{void StackInit(Stack* ps, int n)//不写缺省,但要写对应类型的形参{assert(ps);ps->a = (int*)malloc(sizeof(int)*n);ps->top = 0;ps->capacity = 0;}void StackPush(Stack* ps, int x){//...}
}//Test.cpp文件
#include "Stack.h"
using namespace sjr;
int main()
{//不知道要多少空间Stack st1;StackInit(&st1);//默认开辟4个整型空间//知道要多少空间Stack st2;StackInit(&st2, 100);//此时我们显示传多少,就开多大//使逻辑清晰且知道需要多大空间时减少扩容消耗。return 0;
}

函数声明、定义分离,缺省参数只写在声明里,不能声明定义同时写缺省,这是为了避免声明和定义缺省值不一。

比如在声明里的缺省值是10,在定义里的缺省值是20这种情况。

为什么是在声明里给缺省参数,而不是定义里给:这是因为有定义的地方一定包括声明,有声明的地方不一定有定义。

比如Test.cpp包含头文件Stack.h,如果声明里没有缺省参数,那么到StackInit这个函数的时候,编译器并不知道有没有缺省参数存在。

总结一句话:函数声明定义分离,缺省参数写在声明里。

三.函数重载

函数重载指的是函数名相同、参数类型不同、个数不同、顺序不同的函数。

在这里插入图片描述

同为Add函数名的有两个,从C语言的角度看,main函数里调用的都是同一个函数,这是因为C语言不支持函数重载(仅根据函数名区分函数)。

C++区分函数不仅仅看函数名,还看函数的参数类型、个数、顺序等。C++中以上两个Add函数构成重载,main函数里两个int实参调用int加法,两个double实参调用double加法。

但是如果我们调用Add(1,2.2);时,隐式转换将产生调用歧义(即使构成函数重载,也要注意细节)。接下来我们看缺省参数属于什么类型:

在这里插入图片描述

Func(int a = 0)这个函数参数虽然是缺省参数,但是它的类型依旧是int,也就是所它们两个函数不能构成重载,并且传参调用有二义性。

函数构成重载的条件,类型和个数不同都好理解,顺序不同是什么意思呢?

在这里插入图片描述

​ 总结:函数重载是C++支持的一种不同于C语言的特性,不同函数的函数名可以相同,但参数的类型、个数、顺序需要有不同的。此外函数返回值不参与构成函数重载的评判(调用时用不到),因为调用时无法根据参数列表确定调用哪个重载函数。

四.引用

引用是给已经存在的变量取一个别名,也就是说,一块空间可以用多个名称表示。

在这里插入图片描述

c是a的别名,可以继续给a起别名,也可以给a的别名(c)起别名,都指向同一块空间(a)。并且引用必须初始化不能更改指向。以上是引用的基本使用方法。

那么引用的作用是什么,如果仅使用这个功能是没有意思的,接下来我们看引用的应用场景:

4.1引用做参数
#include <iostream>void Swap(int* left, int* right)
{int tmp = *left;*left = *right;*right = tmp;
}
//left是a的别名,right是b的别名,在函数里的改变会影响外面的a,b
void Swap(int& left, int& right)//int& left 也是int类型
{int tmp = left;left = right;right = left;
}
int main()
{int a = 10;int b = 20;//函数重载Swap(&a, &b);Swap(a, b);return 0;
}

使用引用做参数,形式看起来比较容易,而且熟悉之后,理解起来也很容易。

我们再讲讲之前单链表需要传递二级指针的理解问题,对比使用引用和指针的区别更进一步体会引用做参数带来形式上的简便。

typedef struct SListNode
{int val;struct SListNode* next;
}SLNode,*PSLNode//简写
void SListPushBack(SLNode** pphead, int x)
{if(*pphead == NULL){*pphead = newnode;//要把plist从空指针改成指向第一个结点}else{//找尾tail->next = newnode;// }
}int main()
{SLNode* plist = NULL;SListPushBack(&plist, 1);SListPushBack(&plist, 2);SListPushBack(&plist, 3);SListPushBack(&plist, 4);
}

在尾插时需要判断plist是否为空,为空则要改变SLNode*类型的变量,假如形参部分写着SLNode* phead,那就只是plist的一份拷贝,phead的改变不会影响plist,所以要传二级指针。

那么学了引用,怎么用引用传参呢?请看下面代码:

typedef struct SListNode
{int val;struct SListNode* next;
}SLNode,*PSLNodevoid SListPushBack(SLNode*& phead, int x)
{if(phead == NULL){phead = newnode;//phead是SLNode*类型 是plist的别名,改变phead就会改变plist}else{//找尾tail->next = newnode;// }
}int main()
{SLNode* plist = NULL;SListPushBack(plist, 1);SListPushBack(plist, 2);SListPushBack(plist, 3);SListPushBack(plist, 4);
}

甚至使用上结点指针类型重名命的PSLNode创建变量,让不是很懂引用的初学者糊涂。

这以上是引用做参数的应用场景,使用指针也可以做到,引用和指针在做参数的时候都可以提高效率,只是指针用起来形式更复杂一点。

接下来说引用做返回值的应用场景:

4.2引用做返回值
int Count()
{int n = 0;n++;return n; 
}
int main()
{int ret = Count();return 0;
}

不知道读者有没有思考过,局部变量在出了作用域后生命周期结束。对于上面Count函数里的局部变量n,return n;的时候,Count函数调用结束。

如果返回n的话,那就相当于是访问一个被释放了的空间。其实了解过函数与栈帧的读者知道,n在结束生命周期前拷贝给一个寄存器(由于n变量较小),这个寄存器代替n作为Count的返回值赋值给ret。

也就是说传值返回会进行拷贝,和传值传参一个道理(传值传参会生成一个临时拷贝)。

上述代码使用传值返回是对的,如果使用传引用返回会是怎么样的呢?

int& Count()
{int n = 0;n++;return n; //传引用返回,传n的别名返回
}
int main()
{int ret = Count();//n的别名赋值给ret,相当于把n赋值给ret,因为n的别名指的也是nreturn 0;
}

前面讲过,在return n;的时候,n就被释放掉了。于是返回n赋值给ret的应该是随机值

如果操作系统还没清理n变量这块空间,那么仍有可能保留着1,否则会被刷成随机值,并且根据不同编译器可能还有所不同。

在这里插入图片描述

由于这种随机性,编译器会报出警告。因此返回会销毁的变量时,不采取传引用返回。(注意:会销毁的变量使用引用返回是错误的程序)

接着再进一步看使用引用接收引用返回(会销毁的变量)会如何。

在这里插入图片描述

ret是n的别名,相当于ret指向n那块释放了的空间。那么第一次打印取决于n的空间有没有被清理、取决于是什么编译器,因此是随机值。第二次打印的时候,我们看到那块区域已经被清理了。

在这里插入图片描述

这里执行了Add(3, 4);后,ret打印出来的值就变成了7。这是由于栈空间复用的原因,同一个函数或结构相似的函数连续调用,上一次的栈帧销毁后,立刻为下一次相同函数调用做准备,局部变量的地址不变。

因此ret指向的z的那块空间被第二次的Add函数调用改成7,但是打印出来的也是随机值,具体取决编译器和操作系统,VS2019打印的是7。

以上都是使用引用返回不恰当场景导致的结果,真正使用引用返回的场景是返回不会销毁的。比如malloc在堆上的对象、静态变量等等。

那么引用做返回值的价值是什么:提高效率和可以修改返回值。指针也可以做到,但形式复杂。当然引用还有指针更适合使用的场景,入门篇先不讲。

int main()
{//权限平移const int a = 10;const int& b = a;//权限缩小int c = 20;const int& d = c;//引用可以是常量const int& e = 10;//权限放大const int f = 5;int& g = f;//errorc = f//c是int类型,f是const int会不会有问题?return 0;
}

引用和指针一样存在权限缩放的问题,权限可以平移、可以缩小,不能放大。

f赋值给c是没问题的,它是值拷贝(不存在权限问题),c和f不属于同一块空间,改变c不影响f。

在这里插入图片描述

引用和指针的区别:引用是别名,不开空间,指针存储变量的地址;引用必须初始化,并且不能更改引用对象,指针可以不初始化,也可以更改指向;引用没有空引用,指针有空指针;

五.内联函数

内联函数的关键字是inline,这是用来替代宏函数的。使用内联函数可以使代码量少的函数在调用处展开,避免栈帧创建和销毁的损耗。

宏函数的缺点是:写法复杂(括号较多);宏在预处理阶段就进行替换了,不能调试;没有类型检查;

而使用内联函数避免了宏的缺点,写法就是正常写函数一样,只需在函数返回类型前加inline就变为内联函数,可以进行调试,也有类型检查。

在这里插入图片描述

这里即使Add函数很短,调试进入反汇编还是选择调用,而不是像宏一样展开的原因是,Debug版本下默认内联函数是不会展开的,要把属性修改一下:

在这里插入图片描述

在这里插入图片描述

设置完成后,我们再调试起来看看效果:

在这里插入图片描述

虽说是展开,但不是把Add函数里的代码全部放到调用处,而是编译器实现和函数逻辑一样的指令。

注意内联函数在调用处选不选择展开取决于编译器,不是加了inline关键词的函数就会展开,编译器只会展开代码量少的函数。

这是因为如果有程序员给长代码函数、循环函数、递归函数加上内联,并且编译器无条件展开则会导致需要执行的指令变得非常多,生成的可执行程序文件特别大。

内联函数还有一个特别的点:

//Func.h
#include <iostream>
using namespace std;inline void Func(int a = 10);//Func.cpp
#include "Func.h"void Func(int a)
{cout << a << endl;
}//Test.cpp
#include "Func.h"int main()
{Func();return 0;
}

在这里插入图片描述

出现了链接错误,这是因为内联函数在编译时,Func.cpp文件包含头文件得知Func函数是内联函数后,就没有把函数的地址放进符号表,因为在链接的时候,Test.cpp文件找不到Func函数的地址。

所以当内联函数声明和定义分离时,使用只能在定义的那个文件里使用。

正确的使用内联函数的方法是,将内联函数完整的实现放在头文件中,这样函数就可以在调用的地方直接展开,而不用在链接时候找地址。

六.小语法

6.1auto

auto是C++一个用来自动推导类型的关键字,它的用途是对长类型的省略写法。

在这里插入图片描述

对于指针的写法可以写auto* d = c;,而对于引用我们只能显示写,以上只是说明atuo的用法,对于这种短类型,不是auto的真正使用场景。

注意:auto不能做参数类型,也不能做函数返回类型以及不能用来创建数组。

6.2范围for

对于数组的遍历,C语言使用求数组下标依次遍历,C++使用一个更为简便的语法:

在这里插入图片描述

for(auto e: 数组)这个语法中e是一个和数组元素类型一样的临时变量,将数组里的值依次取出赋值给e,自动判断结束。习惯使用auto当e的类型,让其自动推导类型。

当e作为数组里每个元素的别名时,对其进行修改会影响数组里的元素。

由于冒号后面加的是数组,因此:

#include <iostream>void Func(int arr[])
{for(auto e : arr)//error,arr是首元素地址不是数组{//...}
}
int main()
{int arr[3] = {1,2,3};Func(arr);return 0;
}
6.3空指针

在C++程序中,使用nullptr当做空指针,C语言的NULL有点错误,因此C++委员会后来补上这个坑,引入nullptr这个关键词,实质是void*。

在这里插入图片描述

好了,以上就是C++入门篇,希望读者有所收获!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/630571.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

java基础之设计模式(单例模式,工厂模式)

设计模式 是一种编码套路 单例模式 一个类只能创建一个实例 饿汉式 直接创建唯一实例 package com.by.entity; ​ /*** 单例模式-饿汉式*/ public class ClassA {//static: 1. newClassA可以访问返回 2. 静态属性内存中只会存在一个//private: 防止外界直接访问属性priva…

redis总结--常见问题与解决办法,推荐等级

因为csdn只支持这种文档形式&#xff1a;不支持思维导图&#xff1a; 更好友好的阅读&#xff1a;可以看我的飞书--------思维导图&#xff08;这样食用更加&#xff09;&#xff1a; 缓存穿透问题原因每次从缓存中都查不到数据&#xff0c;而需要查询数据库&#xff0c;同时数…

Kafka-消费者-KafkaConsumer分析-ConsumerCoordinator

在前面介绍了Kafka中Rebalance操作的相关方案和原理。 在KafkaConsumer中通过ConsumerCoordinator组件实现与服务端的GroupCoordinator的交互&#xff0c;ConsumerCoordinator继承了AbstractCoordinator抽象类。 下面我们先来介绍AbstractCoordinator的核心字段&#xff0c;如…

GO——与PHP的并发对比

背景 go比php可支持的并发数更高&#xff0c;为什么 目标 分析点&#xff1a; 系统的并发瓶颈go语言的并发瓶颈php语言的并发瓶颈 系统并发 参考&#xff1a;https://juejin.cn/post/6844904025553534990 提到并发&#xff0c;我们这里指的是web服务web系统的第一层&…

如何在Linux上安装Stable Diffusion WebUI

Stable Diffusion WebUI是一个基于AUTOMATIC1111的stable-diffusion-webui仓库的项目&#xff0c;允许用户通过web界面轻松地生成AI驱动的图像。本文将指导您在Linux系统上完成Stable Diffusion WebUI的安装过程。 准备工作 在安装Stable Diffusion WebUI之前&#xff0c;请确…

Apache JMeter 3.1压力测试监控服务器数据(cpu、内存、磁盘io等)

Apache JMeter 3.1压力测试 Apache JMeter 3.1压力测试监控cpu、内存情况1.下载Apache JMeter 3.11.1 添加线程组1.2 添加http请求1.3 增加http请求头设置1.4 添加csv配置1.5 添加测试结果监控配置 2. 监控插件下载3. 服务端插件下载并启动3.1 下载3.2 解压并启动3.3 增加服务器…

渗透测试之Kali如何利用CVE-2019-0708漏洞渗透Win7

环境: 1.攻击者IP:192.168.1.10 系统: KALI2022(vmware 16.0) 2.靶机IP:192.168.1.8 系统:Windows 7 6.1.7601 Service Pack 1 Build 7601 已开启远程协助RDP服务开启了3389端口 问题描述: KALI 如何利用CVE-2019-0708漏洞渗透Win7 解决方案: 1.打开kali,msf搜索…

最新React面试题:说说对React的理解?有哪些特性?

最新React面试题&#xff1a;说说对React的理解&#xff1f;有哪些特性&#xff1f; 回答思路&#xff1a;是什么&#xff1f;---》特性---》优势是什么&#xff1f;特性声明式编程Component组件的特点 优势 回答思路&#xff1a;是什么&#xff1f;—》特性—》优势 是什么&a…

【每周AI简讯】GPT-5将有指数级提升,GPT Store正式上线

AI7 - Chat中文版最强人工智能 OpenAI的CEO奥特曼表示GPT-5将有指数级提升 GPT奥特曼参加Y-Combinator W24启动会上表示&#xff0c;我们已经非常接近AGI。GPT-5将具有更好的推理能力、更高的准确性和视频支持。 GPT Store正式上线 OpenAI正式推出GPT store&#xff0c;目前…

​一套uni-app + .net医院线上预约挂号系统源码(公众号+小程序预约挂号)

线上预约挂号系统构建了医院和患者的连接&#xff0c;通过改善患者院内的就医服务流程&#xff0c;以微信公众号、支付宝小程序为患者服务入口&#xff0c;为居民提供导诊、预约、支付、报告查询等线上线下一体化的就医服务&#xff0c;缩短患者就诊环节&#xff0c;提高医疗机…

springboot第50集:File类,IO流,网络编程,反射机制周刊

image.png FileReader、FileWriter的使用 FileInputStream、FileOutputStream的使用 image.png image.png image.png image.png image.png image.png image.png image.png image.png image.png image.png image.png 服务器内存优化是一个复杂的过程&#xff0c;通常需要综合考虑…

09前后端分离+SSM整合的小案例

前端的Node 后端的Tomcat&#xff0c;是前端程序的容器。前端的npm 后端的maven 1. 导入前端项目 node版本&#xff1a;16.16.0 配置阿里镜像 npm config set registry https://registry.npmjs.org/ 更新npm版本 npm install -g npm9.6.6 用vscode打开解压后的项目 , 右上角…

Scrum.org认证PSM官方认证班Professional Scrum Master™ (PSM)

课程简介 Scrum是目前运用最为广泛的敏捷开发方法&#xff0c;是一个轻量级的项目管理和产品研发管理框架&#xff0c;旨在最短时间内交付最大价值。根据2022年全球敏捷状态报告&#xff0c;Scrum的应用占比已经达到87%。 Scrum.org 由 Scrum 的联合创始人 Ken Schwaber 创立…

三、Redis命令

一、Redis客户端 Redis是一个客户端和服务器结构的程序&#xff0c;Redis的客户端有很多种形态 通过以下的方法实现Redis客户端和服务器交互&#xff0c;必须先进入Redis-cli客户端程序&#xff0c;才能输入Redis命令。 1、自带了命令行客户端 redis-cli //连接本地 redis-…

jQuery的选择器

目录 基本过滤选择器 层次选择器 简单过滤选择器 内容过滤选择器 可见性过滤器 子元素过滤器 表单对象属性顾虑器 表单选择器 jQuery的选择器分类都有哪些&#xff1f; 根据所获取页面中元素的不同&#xff0c;可以将jQuery选择器分为四大类&#xff1a;基本选择器…

深入了解 Pytest Markers:提升测试用例的组织和控制能力

​从这篇开始&#xff0c;逐一解决fixture是啥&#xff1f;mark是啥&#xff1f;参数request是啥&#xff1f;钩子函数是啥&#xff1f;parametrize参数化是啥&#xff1f;这些问题。本片先介绍一下mark是啥&#xff1f;以及如何使用 Markers有啥用&#xff1f; 当使用 Pytest…

ZooKeeper 实战(五) Curator实现分布式锁

文章目录 ZooKeeper 实战(五) Curator实现分布式锁1.简介1.1.分布式锁概念1.2.Curator 分布式锁的实现方式1.3.分布式锁接口 2.准备工作3.分布式可重入锁3.1.锁对象3.2.非重入式抢占锁测试代码输出日志 3.3.重入式抢占锁测试代码输出日志 4.分布式非可重入锁4.1.锁对象4.2.重入…

SAP PI之Rest adapter

一&#xff0c;简介 REST风格接口是以http为传输协议&#xff0c;以xml或json或text为有效负载。下图展示了REST到XI再返回的一个过程&#xff0c;一个REST接口包含的信息有&#xff1a;服务URL、URL中带的参数、http方法(post/get/put等)、http头部、body部分的有效载荷。而X…

Sentinel限流、熔断

1、限流 单个服务节点限流 sentinel 提供了两种不同的隔离机制&#xff1a;信号量隔离和线程池隔离&#xff0c;它们的主要区别如下&#xff1a; 信号量隔离&#xff08;Semaphore Isolation&#xff09;&#xff1a; 原理&#xff1a;信号量隔离基于计数器&#xff08;或称令…

React Hooks大全—useContext

在本文中&#xff0c;我们将重点介绍useContext这个Hook&#xff0c;它可以让你在函数组件中轻松地访问React Context&#xff0c;从而实现跨组件的状态共享。我们将从基本使用&#xff0c;实现原理&#xff0c;最佳实践&#xff0c;以及一些常见的问题和解决方案来探讨useCont…