【C++篇】迈入新世界的大门——初识C++(下篇)

news2024/9/22 19:29:13

文章目录

  • 前言
  • 引用
    • 引用的概念和定义
    • 引用的特性
    • 引用的使用
    • const引用
    • 指针和引用的关系
  • inline
    • #define定义宏
    • inline
  • nullptr

前言

接上篇:【C++篇】迈入新世界的大门——初识C++(上篇)


引用

引用的概念和定义

引⽤不是新定义⼀个变量,⽽是给已存在变量取了⼀个别名,编译器不会为引⽤变量开辟内存空间, 它和它引⽤的变量共⽤同⼀块内存空间

⽐如:⽔壶传中李逵,宋江叫"铁⽜",江湖上⼈称"⿊旋⻛";林冲,外号豹⼦头;

在这里插入图片描述

引用就是取别名!!!

定义方式

类型&引⽤别名=引⽤对象

C++中为了避免引⼊太多的运算符,会复⽤C语⾔的⼀些符号,⽐如前⾯的<<>>,这⾥引⽤也和取地址使⽤了同⼀个符号&,注意使⽤⽅法⻆度区分就可以。

#include <iostream>
using namespace std;
int main()
{
 int a = 0;
 // 引⽤:b和c是a的别名 
 int& b = a;
 int& c = a;
 // 也可以给别名b取别名,d相当于还是a的别名 
 int& d = b;
 ++d;
 // 这⾥取地址我们看到是⼀样的 
 cout << &a << endl;
 cout << &b << endl;
 cout << &c << endl;
 cout << &d << endl;
 return 0;
}

其实就是一个变量所在的内存空间给它起了不同的名字

在这里插入图片描述


引用的特性

  • 引用在定义时必须初始化
  • 一个变量可以有多个引用
  • 引用一旦引用一个实体,再也不能引用其他实体
#include<iostream>
using namespace std;
int main()
{
    int a = 10;

    // 编译报错:“ra”: 必须初始化引⽤ 
    //int& ra;
    int& b = a;
    int c = 20;
    // 这⾥并⾮让b引⽤c,因为C++引⽤不能改变指向, 
    // 这⾥是⼀个赋值 
    b = c;
    cout << &a << endl;
    cout << &b << endl;
    cout << &c << endl;
    return 0;
}
  • 比如在链表等数据结构,就只能用指针,因为引用无法改变指向(但是在Java中就只使用引用,其引用就可以改变指向)

在这里插入图片描述


引用的使用

  • 引⽤在实践中主要是于引⽤传参和引⽤做返回值中减少拷⻉提⾼效率和改变引⽤对象时同时改变被引⽤对象
  • 引⽤传参跟指针传参功能是类似的,引⽤传参相对更⽅便⼀些。
void Swap(int& rx, int& ry)
{
    int tmp = rx;
    rx = ry;
    ry = tmp;
}
int main()
{
    int x = 0, y = 1;
    cout << x <<" " << y << endl;
    Swap(x, y);
    cout << x << " " << y << endl;
    return 0;
}
  • 引⽤返回值的场景相对⽐较复杂,我们在这⾥简单⼀下场景,还有⼀些内容后续类和对象章节中会继续深⼊讲解。

我们在数据结构介绍了栈,具体的可以看【初阶数据结构篇】栈的实现(附源码)这篇文章

我们这里要取栈顶数据,想直接改变栈顶数据的值,就可以用传引用返回

#include<iostream>
using namespace std;
typedef int STDataType;
typedef struct Stack
{
    STDataType* a;
    int top;
    int capacity;
}ST;
void STInit(ST& rs, int n = 4)
{
    rs.a = (STDataType*)malloc(n * sizeof(STDataType));
    rs.top = 0;
    rs.capacity = n;
}
// 栈顶 
void STPush(ST& rs, STDataType x)
{
    assert(ps);
    // 满了, 扩容 
    if (rs.top == rs.capacity)
    {
        printf("扩容\n");
        int newcapacity = rs.capacity == 0 ? 4 : rs.capacity * 2;
        STDataType* tmp = (STDataType*)realloc(rs.a, newcapacity * 
                                         sizeof(STDataType));
        if (tmp == NULL)
        {
            perror("realloc fail");
            return;
        }
        rs.a = tmp;
        rs.capacity = newcapacity;
    }
    rs.a[rs.top] = x;
    rs.top++;
}
// int STTop(ST& rs)
int& STTop(ST& rs)
{
    assert(rs.top > 0);
    return rs.a[rs.top];
}
int main()
{
    // 调⽤全局的 
    ST st1;
    STInit(st1);
    STPush(st1, 1);
    STPush(st1, 2);
    cout << STTop(st1) << endl;//2
    STTop(st1) += 1;
    cout << STTop(st1) << endl;//3
    return 0;
}
  • 当然也可以用指针,但是显然引用更方便
int* STTop(ST& rs)
{
    assert(rs.top > 0);
    //return &(rs.a[rs.top - 1]);
    return rs.a + (rs.top - 1);
}

补充:

如果是写成下图左边形式->传值返回,编译会直接报错,C++在传值返回时会先把数据拷贝到一份空间中,这个叫做临时对象,C++规定临时对象具有常性,所以不能进行+=操作,会报错。而传引用返回就跳过了这一拷贝步骤,直接返回变量的别名,就可以对变量进行修改了

所谓临时对象就是编译器需要⼀个空间暂存表达式的求值结果时临时创建的⼀个未命名的对象, C++中把这个未命名对象叫做临时对象。

在这里插入图片描述

  • ⼀些主要⽤C代码实现版本数据结构教材中,使⽤C++引⽤替代指针传参,⽬的是简化程序,避开复杂的指针。
#include<iostream>
using namespace std;
typedef struct SeqList
{
    int a[10];
    int size;
}SLT;
 
void SeqPushBack(SLT& sl, int x)
{
    //...
}
  • 指针变量也可以取别名,这⾥LTNode*& phead就是给指针变量取别名,这样就不需要⽤⼆级指针了,相对⽽⾔简化了程序
typedef struct ListNode
{
    int val;
    struct ListNode* next;
}LTNode, *PNode;

//void ListPushBack(LTNode** phead, int x)
//void ListPushBack(LTNode*& phead, int x)
void ListPushBack(PNode& phead, int x)
{
    PNode newnode = (PNode)malloc(sizeof(LTNode));
    newnode->val = x;
    newnode->next = NULL;
    if (phead == NULL)
    {
        phead = newnode;
    }
    else
    {
        //...
    }
}
int main()
{
    PNode plist = NULL;
    ListPushBack(plist, 1);
    return 0;
}

总之:引⽤和指针在实践中相辅相成,功能有重叠性,但是各有特点,互相不可替代。C++的引⽤跟其他语⾔的引⽤(如Java)是有很⼤的区别的,除了⽤法,最⼤的特点就是:C++引⽤定义后不能改变指向, Java的引⽤可以改变指向。


const引用

在这之前先看我们在C语言指针里讲过的:

#include <stdio.h>
int main()
{
	const int a = 5;
	 int* p = &a;
	 *p = 4;
	 printf("%d", a);
	return 0;
}

在这里插入图片描述

  • 在C语言里它不会报错,只会报警告,可以成功运行,这里我们通过指针修改了const修饰的变量,按理来说是不合法的,但由于C的语法检查不是那么严,所以仍然可以运行

在这里插入图片描述

  • 但如果将它改为.cpp,那就会报错了,C++的语法检查更为严格,不允许我们修改const修饰的变量

注意:

下面这种情况不存在权限放大,因为const修饰的是p5本身而不是指向的内容

int b = 5;
int* const p5 = &b;
int* p6 = p5;
*p6 = 15;

在引用中也是类似的,总结就是:权限可以缩小,不能放大

  • 可以引⽤⼀个const对象,但是必须⽤const引⽤。const引⽤也可以引⽤普通对象,因为对象的访问权限在引⽤过程中可以缩⼩,但是不能放⼤。
int main()
{
    const int a = 10;
    // 编译报错: “初始化”: ⽆法从“const int”转换为“int &” 
    // 这⾥的引⽤是对a访问权限的放⼤ 
    //int& ra = a;
    // 这样才可以 
    const int& ra = a;
    // 编译报错: “ra”: 不能给常量赋值 
    //ra++;
    
    // 这⾥的引⽤是对b访问权限的缩⼩ 
    int b = 20;
    const int& rb = b;
    // 编译报错: “rb”: 不能给常量赋值 
    //rb++;
    b++;//这是可以的
    return 0;
}

在这里插入图片描述

  • 不过需要注意的是类似 int& rb = a*3; double d = 12.34; int& rd = d; 这样⼀些场景下例如a*3的结果保存临时对象中, int& rd = d 也是类似,在类型转换中也会产⽣临时对象存储中间值,也就是时,rbrd引⽤的都是临时对象,⽽C++规定临时对象具有常性,所以这⾥就触发了权限放⼤,必须要⽤常引⽤才可以。
#include<iostream>
using namespace std;
int main()
{
    int a = 10;
    const int& ra = 30;

    // 编译报错: “初始化”: ⽆法从“int”转换为“int &” 
    // int& rb = a * 3;
    const int& rb = a*3;
    double d = 12.34;
    // 编译报错:“初始化”: ⽆法从“double”转换为“int &” 
    // int& rd = d;
   
    const int& rd = d;
    return 0;
}

在这里插入图片描述

const引用在传参时很有效:

如下:如果不是const传参,那除了f1(a)都要报错

void f1(const int& rx)
{
    //..
}

int main()
{
    const int xx = 20;
    int a = 10;
    const int& rb = a * 3;

    double d = 12.34;
    const int& rd = d;

    f1(xx);
    f1(a);
    f1(a*3);
    f1(d);

    return 0;
}

指针和引用的关系

C++中指针和引⽤就像两个性格迥异的亲兄弟,指针是哥哥,引⽤是弟弟,在实践中他们相辅相成,功能有重叠性,但是各有⾃⼰的特点,互相不可替代。

  • 语法概念上引⽤是⼀个变量的取别名不开空间,指针是存储⼀个变量地址,要开空间。
  • **引⽤在定义时必须初始化,指针建议初始化,但是语法不是必须的 **。
  • 引⽤在初始化时引⽤⼀个对象后,就不能再引⽤其他对象;⽽指针可以在不断地改变指向对象。
  • 引⽤可以直接访问指向对象,指针需要解引⽤才是访问指向对象
  • sizeof中含义不同,引⽤的结果为引⽤类型的⼤⼩,但指针始终是地址空间所占字节个数(32位平台下占4个字节,64位下是8字节)
int main()
{
    int& r;
    int* p;

    char x = 'a';
    char& rx = x;
    cout << sizeof(rx) << endl;
    char* ptr = &x;
    cout << sizeof(ptr) << endl;

    return 0;
}
  • 指针很容易出现空指针和野指针的问题,引⽤很少出现,引⽤使⽤起来相对更安全⼀些。

但是引用也会存在“野引用”
如下:

int& func()
{
	int ret = 10;
	ret++;
	//...

	return ret;//函数返回后销毁,此时此块空间已经还给操作系统,不能使用
}

只是相对来说更安全一些

补充:

在第一条我们是这么说的:

语法概念上引⽤是⼀个变量的取别名不开空间,指针是存储⼀个变量地址,要开空间。

但在实际的底层汇编代码来看,引用也是通过指针实现的

int main()
{
	int x = 0;
	int& rx = x;
	rx += 1;

	int* ptr = &x;
	*ptr += 1;

	return 0;
}

在这里插入图片描述

知道一下就可以了😁


inline

#define定义宏

在这之前我们先回顾一下C中的#define定义的宏
更多的详细内容请看【C语言篇】编译和链接以及预处理介绍(上篇)

 #define N 10

//实现一个ADD宏函数的常见问题
#define ADD(int a, int b) return a + b;
#define ADD(a, b) (a) + (b);
#define ADD(a, b) (a + b)

// 正确的宏实现
#define ADD(a, b) ((a) + (b))

#define ADD(a, b) ((a) + (b))
// 为什么不能加分号?
// 为什么要加外面的括号?
// 为什么要加里面的括号?

int main()
{
	int ret = ADD(1, 2); // int ret = ((1) + (2));;
	cout << ret << endl;

	cout << ADD(1, 2) << endl;
	if (ADD(1, 2))//加分号报错
	{}

	cout << ADD(1, 2)*3 << endl;//要加外面的括号

	int x = 1, y = 2;
	ADD(x & y, x | y); // -> (x&y+x|y)//+优先级高于位运算符,要加里面的括号
	
    
	ADD(x & y, x | y); // -> ((x&y)+(x|y))
    //这才是正确的

	return 0;
}

总结: 宏函数坑很多,但是替换机制,调用时不用建立栈帧,提效

C语⾔实现宏函数也会在预处理时替换展开,但是宏函数实现很复杂很容易出错的,且不⽅便调试,于是祖师爷在C++设计inline⽬的就是替代C的宏函数。


inline

  • inline修饰的函数叫做内联函数,编译时C++编译器会在调⽤的地⽅展开内联函数,这样调⽤内联函数就不需要建⽴栈帧了,就可以提⾼效率。

  • inline对于编译器⽽⾔只是⼀个建议,也就是说,你加了inline编译器也可以选择在调⽤的地⽅不展开,不同编译器关于inline什么情况展开各不相同,因为C++标准没有规定这个。

  • inline适⽤于频繁调⽤的短⼩函数,对于递归函数,代码相对多⼀些的函数,加上inline也会被编译器忽略。

#include<iostream>
using namespace std;
inline int Add(int x, int y)
{
    int ret = x + y;
    ret += 1;
    ret += 1;
    ret += 1;
    return ret;
}
int main()
{
    // 可以通过汇编观察程序是否展开 
    // 有call Add语句就是没有展开,没有就是展开了 
    int ret = Add(1, 2);
    cout << Add(1, 2) * 5 << endl;
    return 0;
}

在这里插入图片描述

把选择权交给编译器自己,保证程序的效率
如以下情况,如果选择权交给程序员,万一不靠谱的程序员写成了inline函数,那展开后的指令数将超多,造成不可预料的结果😂
在这里插入图片描述

  • vs编译器debug版本下⾯默认是不展开inline的,这样⽅便调试,debug版本想展开需要设置⼀下以下两个地⽅。

在这里插入图片描述

  • inline不建议声明和定义分离到两个⽂件,分离会导致链接错误。因为inline表示函数需要被展开,就不是函数地址调用的,链接时会出现报错。
// F.h
#include <iostream>
using namespace std;
inline void f(int i);

// F.cpp
#include "F.h"
void f(int i)
{
    cout << i << endl;
}

// main.cpp
#include "F.h"
int main()
{
    // 链接错误:⽆法解析的外部符号 "void __cdecl f(int)" (?f@@YAXH@Z) 
    f(10);
    return 0;
}

正确的使用方式:在头文件中声明定义一起写:

#pragma once
#include <iostream>
using namespace std;

inline void f(int i)
{
	cout << i << endl;
}

nullptr

NULL实际是⼀个宏,在传统的C头⽂件(stddef.h)中,可以看到如下代码:

#ifndef NULL
    #ifdef __cplusplus
    	#define NULL 0
    #else
    	#define NULL ((void *)0)
    #endif
#endif
  • C++中NULL被定义为字⾯常量0
  • C中NULL被定义为⽆类型指针(void*)的常量

不论采取何种定义,在使⽤空值的指针时,都不可避免的会遇到⼀些⿇烦

如下:

#include<iostream>
using namespace std;
void f(int x)
{
    cout << "f(int x)" << endl;
}
void f(int* ptr)
{
    cout << "f(int* ptr)" << endl;
}
int main()
{
    f(0);
    // 本想通过f(NULL)调⽤指针版本的f(int*)函数
    //但是由于NULL被定义成0,调⽤了f(int x),因此与程序的初衷相悖。 
    f(NULL);
    f((int*)NULL);
    // 编译报错:“f”: 2 个重载中没有⼀个可以转换所有参数类型 
    // f((void*)NULL);

    f(nullptr);
    return 0;
}

本想通过f(NULL)调⽤指针版本的 f(int*)函数,但是由于NULL被定义成0,调⽤了f(intx),因此与程序的初衷相悖。而如果想把NULL强转为指针类型f((void*)NULL); 调⽤会报错。 (C++规定void*类型的指针不能被隐式转换成任何类型的指针(必须显示转换))

其实根本原因就是:在这之前C/C++标砖规定的NULL既是整数常量,也是空指针常量

所以C++11中引⼊nullptrnullptr是⼀个特殊的关键字,nullptr是⼀种特殊类型的字⾯量,它可以转换成任意其他类型的指针类型。使⽤nullptr定义空指针可以避免类型转换的问题,因为nullptr只能被隐式地转换为指针类型,⽽不能被转换为整数类型。

所以在之后我们都是用nullptr来初始化空指针


以上就是关于【C++篇】迈入新世界的大门——初识C++(下篇)的内容啦,各位大佬有什么问题欢迎在评论区指正,您的支持是我创作的最大动力!❤️

在这里插入图片描述

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

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

相关文章

第10章 无持久存储的文件系统 (3)

目录 10.2 简单文件系统 10.2.1 顺序文件 10.2.2 用libfs编写文件系统 10.2.3 调试文件系统 10.2.4 伪文件系统 10.3 sysfs 10.3.1 概述 10.3.2 数据结构 10.3.3 装载文件系统 10.3.4 文件和目录操作 10.3.5 向sysfs添加内容 10.4 小结 本专栏文章将有70篇左右&…

Node.js及mysql的安装,建立页面,javascript对mySQL数据库的操作过程

具体动态效果看视频 node.js连接MySQL数据库操作 第一部分&#xff1b;配置服务器环境 Nods.js, NPM,CNPM,mysql2,express的安装 前往 Node.js 官方网站&#xff08;https://nodejs.org/&#xff09;下载并安装最新的稳定版本&#xff0c;确定配置好path环境变量&#xff0c;其…

Linux网络环境搭建,开发板网线直连电脑网口,电脑WIFI上网

开发板网线直连电脑网口&#xff08;电脑自带&#xff0c;一般有PCI&#xff0c;不是USB网卡&#xff09;&#xff0c;电脑WIFI上网 因为电脑是 WiFi 上网&#xff0c;所以需要添加一个网络适配器并设置成 NAT 模式&#xff0c;供虚拟机上网。 设置双网卡&#xff0c;注意双网卡…

SQL 时间盲注 (injection 第十五关)

简介 SQL注入&#xff08;SQL Injection&#xff09;是一种常见的网络攻击方式&#xff0c;通过向SQL查询中插入恶意的SQL代码&#xff0c;攻击者可以操控数据库&#xff0c;SQL注入是一种代码注入攻击&#xff0c;其中攻击者将恶意的SQL代码插入到应用程序的输入字段中&#x…

visual studio使用技巧:快速生成Json、XML对应类

visual studio快速生成Json、XML对应类 在项目中经常用到json或者xml作为配置文件&#xff0c;进行序列化和反序列化就需要有对应的类&#xff0c;重新写一遍类就比较麻烦&#xff0c;这里就讲一下通过visual studio快速生成json或者xml对应类型的方法。 自动生成Json类 复制…

大数据-90 Spark 集群 RDD 编程-高阶 RDD容错机制、RDD的分区、自定义分区器(Scala编写)、RDD创建方式

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; 目前已经更新到了&#xff1a; Hadoop&#xff08;已更完&#xff09;HDFS&#xff08;已更完&#xff09;MapReduce&#xff08;已更完&am…

【Python】AttributeError: module ‘PIL.Image‘ has no attribute ‘ANTIALIAS‘

【Python】成功解决AttributeError: module ‘PIL.Image‘ has no attribute ‘ANTIALIAS‘ 下滑即可查看博客内容 &#x1f308; 欢迎莅临我的个人主页 &#x1f448;这里是我静心耕耘深度学习领域、真诚分享知识与智慧的小天地&#xff01;&#x1f387; &#x1f393; 博…

MySQL集群+Keepalived实现高可用部署

Mysql高可用集群-双主双活-myqlkeeplived 一、特殊情况 常见案例&#xff1a;当生产环境中&#xff0c;当应用服务使用了mysql-1连接信息&#xff0c;在升级打包过程中或者有高频的数据持续写入【对数据一致性要求比较高的场景】&#xff0c;这种情况下&#xff0c;数据库连接…

STM32之继电器与震动传感器的使用,实现震动灯

在STM32的外设应用中&#xff0c;继电器扮演着重要的角色。继电器作为一种电控制器件&#xff0c;其主要作用是通过小电流控制大电流的通断&#xff0c;实现电路的自动控制和保护。具体来说&#xff0c;继电器在STM32外设中的作用可以归纳为以下几点&#xff1a; 电路隔离与保…

在线学习考试设计与实现(源码+lw+部署文档+讲解等)

文章目录 前言具体实现截图详细视频演示技术栈系统测试为什么选择我官方认证玩家&#xff0c;服务很多代码文档&#xff0c;百分百好评&#xff0c;战绩可查&#xff01;&#xff01;入职于互联网大厂&#xff0c;可以交流&#xff0c;共同进步。有保障的售后 代码参考数据库参…

“CSS”第一步——WEB开发系列13

CSS (Cascading Style Sheets&#xff0c;层叠样式表&#xff09;&#xff0c;是一种用来为结构化文档&#xff08;如 HTML 文档或 XML 应用&#xff09;添加样式&#xff08;字体、间距和颜色等&#xff09;的计算机语言&#xff0c;CSS 文件扩展名为 .css。 一、什么是 CSS&a…

ubuntu x86_64系统上安装运行aarch系统的虚拟机

安装qemu-system-aarch64 创建sda.qcow2 虚拟磁盘 运行命令启动虚拟机 sudo qemu-system-aarch64 -M virt-4.0 -m 4G -cpu cortex-a57 -bios /usr/share/qemu-efi-aarch64/QEMU_EFI.fd -cdrom ~/下载/openEuler-24.03-LTS-aarch64-dvd.iso -drive ifnone,filesda.qcow2,idhd0…

王老师 linux c++ 通信架构 笔记(五)编译后生成的 nginx 可执行程序的启动

&#xff08;22&#xff09; 启动 nginx &#xff1a; 上网测试一下&#xff1a; 端口号 介绍&#xff1a; &#xff08;23&#xff09; 因为 nginx 监听知名端口号 80 &#xff0c;http 服务。也可以知名端口号&#xff0c;格式如下&#xff1a; 生产环境下可以设置 ngi…

Pulsar官方文档学习笔记——架构概览

架构概览 在最高配置下&#xff0c;pulsar服务应该由一个或多个pulsar集群组成。 一个pulsar集群可以包括如下组件 一个或多个broker。broker会将生产者 的消息分派给消费者。与pulsar配置存储通信来协调各种任务。将消息 存储在 BookKeeper实例中 &#xff08;也可以叫book…

计算机毕业设计选什么题目好?springboot 基于Java的学院教学工作量统计系统

✍✍计算机毕业编程指导师 ⭐⭐个人介绍&#xff1a;自己非常喜欢研究技术问题&#xff01;专业做Java、Python、微信小程序、安卓、大数据、爬虫、Golang、大屏等实战项目。 ⛽⛽实战项目&#xff1a;有源码或者技术上的问题欢迎在评论区一起讨论交流&#xff01; ⚡⚡ Java、…

java生成随机数字,生成随机ID

java在代码中生成随机数字和ID的两个方法 import java.util.UUID; import java.util.Random; public class randomID {public static void main(String[] args) {// TODO Auto-generated method stubUUID uuid UUID.randomUUID();String randomId uuid.toString();System.ou…

Qt自定义控件之提升法

1、参考&#xff1a;Qt之实现自定义控件的两种方式——提升法 2、概述&#xff1a;自定义控件是常需要使用到的技能&#xff0c;在既有的Qt控件不能满足开发的前提下&#xff0c;自定义控件给了程序员很多的发挥空间和便利。自定义控件有两种方式&#xff0c;一种是通过提升法来…

记录win10下 yolov8 tensorrt模型部署

前言 我的环境是 CUDA11.6 cudnn8.4 python3.8 vs2022 tensorRT8.4.2.4 实现 参考了下面这个视频和文章&#xff08;跟着视频做为主&#xff0c;文章为辅&#xff09;一遍成功&#xff0c;因为这个博主写的很详细&#xff0c;很赞&#xff0c;我就不再重复去写了。 视频&…

C++ Primer 总结索引 | 第十八章:用于大型程序的工具

1、大规模应用程序的特殊要求包括&#xff1a; 在独立开发的子系统之间 协同处理错误的能力使用各种库&#xff08;可能包含独立开发的库&#xff09;进行 协同开发的能力对比较复杂的应用 概念建模的能力 对应 异常处理、命名空间和多重继承 1、异常处理 1、异常处理机制 …

【算法】令牌桶算法

一、引言 令牌桶算法&#xff08;Token Bucket Algorithm, TBA&#xff09;是一种流行于网络通信领域的流量控制和速率限制算法。它允许一定程度的突发传输&#xff0c;同时限制长时间内的传输速率。令牌桶算法广泛应用于网络流量管理、API请求限流等场景。其基本原理是通过一个…