【C++学习】栈 | 队列 | 优先级队列 | 反向迭代器

news2025/1/23 13:11:29

🐱作者:一只大喵咪1201
🐱专栏:《C++学习》
🔥格言:你只管努力,剩下的交给时间!
图

栈 | 队列 | 优先级队列 | 反向迭代器

  • 😼容器适配器
    • 🙈什么是适配器
    • 🙈STL标准库中stack和queue的底层结构
      • 简单了解deque
  • 😼stack
    • 🙈模拟实现
  • 😼queue
    • 🙈模拟实现
  • 😼priority_queue
    • 🙈仿函数(函数对象)
    • 🙈模拟实现
  • 😼反向迭代器
  • 😼总结

这篇文章主要讲解的是栈和队列以及优先级队列的模拟实现,要像模拟实现,必须先了解容器适配器。

😼容器适配器

🙈什么是适配器

适配器是一种设计模式,设计模式是指:一套反复使用的,多人知晓的,经过分类编目的,代码设计经验的总结。

我们之前就接触过一种设计模式,叫做迭代器,再加上现在的适配器,一共是两种设计模式。

  • 迭代器:不暴露底层细节,封装后提供统一的方式访问容器的一种模式。
  • 适配器:将一个类的接口转换成客户希望的另外一个接口的模式。

图
就像我们生活中使用的电源适配器一样,已经有的接口是家里的插口,它是220V的,而我们经常使用的电子设备用到的电压远没有这么高,所以就需要将电压进行转换,转换成我们能用的低电压。

比如手机充电器,它就将220V电压转换到了手机能承受的低电压。

🙈STL标准库中stack和queue的底层结构

虽然stack和queue中也可以存放元素,但在STL中并没有将其划分在容器的行列,而是将其称为容器适配器,这是因为stack和queue列只是对其他容器的接口进行了包装。

图
红色框中的是容器类型,也就是被改造容器,栈和队列使用的容器是双端队列(deque),优先级队列使用的是vector。栈和队列就是将这些已有容器的接口进行改造,符合我们的要求,这种改造容器出来的结构就是容器适配器。

简单了解deque

  • deque(双端队列):是一种双开口的"连续"空间的数据结构。
  • 可以在头尾两端进行插入和删除操作,且时间复杂度为O(1),与vector比较,头插效率高,不需要搬移元素;与list比较,空间利用率比较高。

图
这是它的成员函数。
图
它的逻辑结构如上图所示,但实际上并不是连续的空间,而是由多段连续的小空间拼接成的。

图

  • 中控指针数组中存放着多段小连续数组空间的地址,所以它是一个指针数组。
  • 连续数据空间的地址是从中控数组的中间位置开始存放的,之后开辟的小连续数组空间的地址向右或者向左存放。
  • 当小连续数组被放满数据以后,就会开辟新的小连续数组。
  • 如果是尾插,则将新的小连续数组的指针放在中控指针数组中间位置的右边,并且将元素从小连续数组的头部开始插入,直到满了。
  • 如果是头插,则将新的小连续数组的指针放在中控指针数组中间位置的坐标,并且将元素从小连续数组的尾部开始插入,直到满了。
  • 当中控指针数组存满数据以后,就会将中控指针数组进行扩容,用了存放更多小连续数组的指针。

大概知道了它的存储结构以后,将deque与vector和list作个对比。

  • 与vector比较:deque在头部插入和删除时,不需要搬移元素,效率特别高,而且在扩容时,也不需要搬移大量的元素,只需要搬移指针就行,因此其效率是比vector高的。
  • 与list比较:deque的底层是连续空间,空间利用率比较高,高速缓存命中率高,而且不需要存储额外字段,最重要的是deque支持下标随机访问。
  • 但是,deque有一个致命缺陷:不适合遍历。
  • 因为在遍历时deque的迭代器要频繁的去检测其是否移动到某段小空间的边界,导致效率低下。
  • 除此之外,在使用下标随机访问时,还需要根据下标计算出这个下标在哪个小连续数组中,有一定的消耗,速度没有vcetor快。
  • 而且在中间插入删除时,虽然没有挪动大量的数据,但也是有一定的消耗,速度没有list快。

虽然deque结合了vector和list的优点,但是都没有做到vector和list那么极致,所以在实际中,需要线性结构时,大多数情况下优先考虑vector和list。

deque的应用并不多,而目前能看到的一个应用就是,STL用其作为stack和queue的底层数据结构进行改造。

😼stack

tu
stack是一个后进先出的数据结构,它不是容器,而是容器适配器。

常用成员函数:

图
有了string,vector,list的基础,看到这些接口应该会非常熟悉,并且知道它们怎么用。

本喵简单演示下怎么使用:

#include <stack>
#include <iostream>
using namespace std;

int main()
{
	stack<int> st1;
	st1.push(1);
	st1.push(2);
	st1.push(3);
	st1.push(4);
	st1.push(5);

	while (!st1.empty())
	{
		cout << st1.top() << " ";
		st1.pop();
	}
	cout << endl;

	return 0;
}

tu
入栈顺序是12345,出栈顺序是54321。

🙈模拟实现

图

使用泛型编程,将适配器要改造的容器类型设置成模板参数Container,并且给它一个缺省参数,如果没有指定被改造容器类型就使用deque。

  • stack容器适配器就是要将已有的容器接口进行改造,从而符合我们对要求,所以成员变量只有一个容器类型。

stack类中不需要有显式的构造函数,因为编译器自动生成的默认构造函数就能满足要求。

  • 自动生成的默认构造函数对自定义类型调用它的默认构造函数。
  • 对内置类型不做处理。

我们这里没有内置类型,只有一个自定义类型,而这个自定义类型还是一个已有的容器,所以会调用它的默认构造函数来进行初始化。

接下来就是改造已有容器的接口,让它符合我们对要求。

图
上图所示代码就是stack那些接口的模拟实现。这些接口都是根据栈的特性来写的。

  • 栈的特性:先进后出,插入数据时尾插,出数据时尾删,访问数据只能访问栈顶数据,也就是尾部的数据。

改造的已有容器我们使用的是模板,给了它一个缺省值,默认使用deque作为被改造容器。

既然是模板参数,而且是一个缺省值,那么我们也可以指定被改造的容器类型。

被改造容器必有接口改造后的接口
push_back()push()
pop_back()pop()
back()top()
empty()empty()
size()empty()

有这些接口的容器,目前我们接触到的而且比较合适的也就是vector,list以及deque。

图
使用默认deque的容器适配器stack。

图
使用指定vector的容器适配器stack。

图
使用指定list的容器适配器stack。

虽然STL标准库中默认的也是deque,但是我们在使用的时候也可以像上面一样指定被改造的容器,对于栈来说,只要能接受它扩容造成的空间浪费,是可以使用vector的。

😼queue

图
queue是一个先进先出的结构,他同样是一个容器适配器。

常用接口:

图
简单演示:

void Queue_test()
{
	queue<int> q;
	q.push(1);
	q.push(2);
	q.push(3);
	q.push(4);
	q.push(5);

	while (!q.empty())
	{
		cout << q.front() << " ";
		q.pop();
	}
	cout << endl;
}

图

进队列的顺序是1234,出队列的顺序也是1234。

🙈模拟实现

图

和栈一样,同样使用模板,被改造的容器给一个确实值,默认使用deque,成员变量只有被改造的容器。

同样不需要显式构造函数,编译器自动生成的默认构造函数就能够满足需求。

下面实现它的各种接口:

图
同样是对已有容器的接口进行改造。

已有容器必有接口改造后接口
push_back()push()
pop_frontpop()
back()back()
front()front()
empty()empty()
size()size()

和栈一样,有这些接口的容器,我们学习过的,并且合适的只有list和deque,vector是没有front()接口的。

体
默认使用deque的容器适配器queue。

图
指定list的容器适配器queue。

queue和satck有一点不同的是,queue不能使用vector作为被改造的容器,因为vector中没有front()接口,而queue是需要用到这个接口的。

虽然STL中使用的默认容器是deque,但是也可以指定list,但是使用list的话就得接受它的缺点,所以说使用deque更好。

😼priority_queue

图
优先级队列其实就是我们曾经学过的堆,关于堆的内容,在本喵的文章堆的使用原理中有详细介绍,有兴趣的小伙伴可以去看看。

  • 优先队列是一种容器适配器,根据严格的排序标准,它的第一个元素总是它所包含的元素中最大或者最小的。
  • 此结构类似于堆,在堆中可以随时插入元素,并且只能检索最大或者最小堆元素(优先队列中位于顶部的元素)。
  • 同样也是容器适配器,它默认采用的容器是vector。

我们知道,堆的物理结构是数组,而逻辑结构是一个完全二叉树,所以说,vector是最适合优先级队列的已有容器。

常用接口:

图
简单使用:

void Priority_queue_test()
{
	priority_queue<int> pq;
	pq.push(3);
	pq.push(5);
	pq.push(6);
	pq.push(2);
	pq.push(1);

	while (!pq.empty())
	{
		cout << pq.top() << " ";
		pq.pop();
	}
	cout << endl;
}

tu

  • 在将数据插入优先队列的时候,时乱序插入的。
  • 输出到时候,这些数据按照从大到小的顺序输出。

优先级队列是一个堆,按照输出结果来看,第一个堆顶数据是6,第二个堆顶的数据时5,第三个是3以此类推。

还原一下这几个数据入队列后的堆结构:

图

可以看到,这是一个大根堆。

  • 优先级队列,默认情况下是按照大根堆的方式构建的。

那么怎么让它成为小根堆呢?

图
输出结果中,第一个堆顶数据是1,第二个是2,以此类推。

来看它的堆结构:

图
可以看到这是一个小根堆。

  • 在创建priority_queue时,模板实例化传了三个参数。
  • 第一个是插入的数据类型。
  • 第二个是底层容器的类型。
  • 第三个是仿函数。

是大根堆还是小根堆,主要的依据就这个仿函数,它决定了内部的比较方式。

🙈仿函数(函数对象)

图

  • 红色框中,是一个比较函数,两个数相比,如果第一个比第二个大,则返回真。
  • 在Func_test函数中,调用上面的比较函数来进行两个数比较,站在这个函数的角度,它不知道比较的方式是什么,只是使用它的结果。

图
在主函数中,调用Func_test函数的时候,还需要将比较函数当作实参传过去。也就是主函数告诉子函数用Greater去比较,但是不告诉子函数具体的比较细节。

  • 这种方式就是我们自己学习过的回调函数。
  • 在子函数中,通过函数指针的方式来调用这个比较函数。

此时,比较函数的两个形参类型是确定的,只能是int类型,如果要比较别的类型就不行了。

而且函数指针的使用感觉不是很方便,这属于C语言的糟粕,所以C++为了弥补,就有了仿函数。

  • 仿函数:可以像函数一样使用,但是不是真的函数。
template<class T>
class greater
{
public:
	bool operator()(const T& x, const T& y) const
	{
		return x > y;
	}
};

写这样的一个类模板,第一个数比第二个大时返回真。

  • 使用泛型编程,编译器自动推演数据类型。
  • 类中没有成员变量,只有一个成员函数,类对象大小为1字节。
  • 使用operator重载函数运算符括号。

图

此时使用仿函数同样实现了函数指针的功能,而且还支持不同类型的数据,比函数指针好用。

template<class T>
class less
{
public:
	bool operator()(const T& x, const T& y) const
	{
		return x < y;
	}
};

趁热打铁,将第一个数小于第二个数时返回真的仿函数也写出来。

🙈模拟实现

图

优先队列也是容器适配器,所以和前面的成员变量一样,只是默认的被改造容器使用的vector。

除此之外,还有一个模板参数是仿函数,默认使用less,也就是第一个数比第二个数小返回真,因为优先级队列要建堆,涉及到了比较,所以要给它一个比较依据。

因为要建大根堆和小根堆,所以就需要向上调整和向下调整数据,先来实现一下这两个函数。

向上调整:

void adjust_up(size_t child)
{
	Compare com;
	size_t parent = (child - 1) / 2;

	while (child > 0)
	{
		if (com(_con[parent], _con[child]))
		{
			swap(_con[parent], _con[child]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else
			break;
	}
}

假设仿函数使用less,建立的是大根堆。

  • 父节点比子节点小,父子节点交换位置。
  • 交换后父节点成为新的子节点,再和他的父节点进行比较。
  • 直到子节点到了根的位置,说明这次的向上调整结束。

向下调整:

void adjust_down(size_t parent)
{
	Compare com;
	size_t child = parent * 2 + 1;
	while (child < _con.size())
	{
		if (child + 1 < _con.size() && com(_con[child],_con[child + 1]))
		{
			child++;
		}

		if (com(_con[parent], _con[child]))
		{
			swap(_con[parent], _con[child]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
			break;
	}
}

假设仿函数使用less,建立的是大根堆。

  • 父节点比子节点小,父子节点交换位置。
  • 交换后的子节点成为新的父节点,继续和它的子节点比较。
  • 直到子节点的下标超出数组size,说明这次向下调整结束。

这里简单说明一下,详细内容可看本喵另一篇文章堆的使用原理。

图
可以看到,priority_queue是有构造函数的,所以要实现一下。

图
使用迭代器区间初始化的构造函数,在初始化列表中,将底层容器初始化以后,它是无序的,而堆是有序的,所以采用向下调整建堆。

  • 使用向下调整建堆,时间复杂度是O(N*logN)。
  • 这里必须要显式写一个无参的构造函数。

因为已经有使用迭代器区间的构造函数了,此时编译器就不会自动生成无参的默认构造函数了,如果不用迭代器区间初始化去创建一个优先级队列对象,就会导致没有构造函数可掉,会报错没有合适的构造函数。

图
这是其他接口的模拟实现。

底层容器必有接口改造后接口
push_back()push()
pop_back()pop()
operator[]top()
empty()empty()
size()size()

在向堆中插入数据和删除数据时,都需要重新调整堆结构,使其符合大堆或者小堆。

插入数据堆调整:

  • 新插入数据实际上进行了尾插,插入到了数组的最后一个位置,也就是二叉树最后一个元素。
  • 要想继续符合堆结构,就需要将这个新的数据进行向上调整,让它位于正确的位置。

删除数据堆调整:

  • 删除数据删除的是数组的第一个元素,如果直接删除掉,不仅破环了堆结构,而且还需要进行大量的数据挪动。
  • 所以将第一个元素和最后一个元素的位置互换,进行尾删,然后将第一个元素向下调整,让它位于正确的位置。

tu
默认使用的底层容器是vector,默认使用的仿函数是less。

图
指定底层结构是vector,知道仿函数是less,和默认情况一样。

图
指定底层容器vector,指定仿函数greater。

说明:

图
priority_queue类模板的三个参数中,只有第一个不是缺省值,后两个都是缺省值。

  • 虽然只需要给第一个和第三个传参就行。
  • 但是和函数一样,给带有缺省参数的函数或模板传参时,只能从左向右传。

所以要想指定仿函数,还需要指定底层容器。

😼反向迭代器

我们模拟实现string,vector,list等结构的时候,都没有模拟实现过反向迭代器,是因为反向迭代器最好的实现方式也是使用适配器。

既然是适配器,那么它改造的是谁呢?改造的是正向的迭代器,无论是普通反向迭代器还是const反向迭代器,改造的都是普通正向迭代器。

图
改造的底层容器是正向迭代器,所以第一个模板参数是一个迭代器,具体是list迭代器还是vector迭代器,并不知道,所以采用的是泛型编程。

第二个和第三个模板参数和list中迭代器一样,是为了简化const反向迭代器的代码而加的模板参数。

图

被改造容器接口改造后的接口
operator*()operator*()
operator–()operator++()
operator++()operator–()
operator!=()operator!=()
  • 反向迭代器需要构造函数,因为迭代器都是迭代器来初始化的。

图
在进行解引用运算符重载的时候,先进行了减减,然后才进行的解引用。

图
无论是链表还是vector,它们的正向迭代器和反向迭代器都是对称的。

  • begin()和rend()指向同一个位置。
  • end()和rbegin()指向同一个位置。

当解引用rbegin()的时候,此时得到的内容并不是结构中的内容,所以要先进行end()减减,再解引用。

当解引用rend()的时候,此时得到的是首元素的内容,但是我们要的是结构之外的第一个内容,所以要进行begin()减减,在解引用。

所以说,反向迭代器的解引用,要将底层容器的迭代器先减减,再解引用。

在list中验证:

图
在我们曾经写的list中,将reverse_iterator和const_reverse_iterator定义出来,和普通迭代器一样,如上图中红色框中所示。

图
再在list中写获得rbegin,rend以及crbegin,crend的接口,根据前面的分析,是用正向迭代器对称初始化的,如上图所示。

图
可以和标准库中的反向迭代器一样使用。

图
可以和标准库中的const反向迭代器一样使用。

图
const的迭代器同样不可以被修改。

在vector中验证:

图
在我们之前写的vector中,定义俩种反向迭代器,如上图中红色框中所示。

图
和list一样,写对应的接口函数,如上图中所示。

图
和标准库中的反向迭代器一样,可以正常使用。

图
修改const迭代器指向的内容时,也会报常量不可修改的错误。

  • 反向迭代器我们只写了一份。
  • 底层容器是list迭代器和vector的迭代器都可以正常使用。

😼总结

这篇文章主要讲解的就是适配器模式,无论是stack,queue,priority_queue,还是迭代器,它们都是容器适配器,这是底层的容器不一样,而且我们也发现,适配器是真的好用。

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

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

相关文章

数据清洗和特征选择

数据清洗和特征选择 数据清洗和特征挖掘的工作是在灰色框中框出的部分&#xff0c;即“数据清洗>特征&#xff0c;标注数据生成>模型学习>模型应用”中的前两个步骤。 灰色框中蓝色箭头对应的是离线处理部分。主要工作是 从原始数据&#xff0c;如文本、图像或者应…

MySQL的基本语句(SELECT型)

基本MySQL语句SELECTSELECT FROM 列的别名去除重复行空值着重号算术运算符加法( )减法( - )乘法( * )除法&#xff08; / 或DIV)求模&#xff08; % 或MOD)比较运算符等于&#xff08; &#xff09;安全等于&#xff08; <> &#xff09;不等于&#xff08; ! 或 <…

WindTerm 界面/UI字体大小调节

文章目录WindTerm 界面/UI字体大小调节问题&#xff1a;解决办法&#xff1a;第一部分&#xff1a;调整编码部分字体大小第二部分&#xff1a;调整UI界面字体大小WindTerm 界面/UI字体大小调节 问题&#xff1a; 今天在使用windTerm的时候&#xff0c;发现windterm界面字体过…

MySQL基础篇1

第1章 数据库介绍 1.1 数据库概述 什么是数据库&#xff1f; 数据库就是存储数据的仓库&#xff0c;其本质是一个文件系统&#xff0c;数据按照特定的格式将数据存储起来&#xff0c;用户可以对数据库中的数据进行增加&#xff0c;修改&#xff0c;删除及查询操作。 数据库分两…

多线程的Thread 类及方法

✨个人主页&#xff1a;bit me&#x1f447; ✨当前专栏&#xff1a;Java EE初阶&#x1f447; ✨每日一语&#xff1a;海压竹枝低复举&#xff0c;风吹山角晦还明。 目 录&#x1f332;一. 线程的复杂性&#x1f334;二. Thread 类及常见方法&#x1f4d5;2.1 Thread 的常见构…

Dubbo的服务暴漏与服务发现源码详解

服务暴漏 如果配置需要刷新则根据配置优先级刷新服务配置 如果服务已经导出&#xff0c;则直接返回 是否异步导出&#xff08;全局或者服务级别配置了异步&#xff0c;则需要异步导出服务&#xff09; 服务暴漏入口DefaultModuleDeployer#exportServices private void exp…

Redis缓存穿透

缓存穿透&#xff1a; 缓存穿透说简单点就是⼤量请求的 key 根本不存在于缓存中&#xff0c;导致请求直接到了数据库上&#xff0c; 根本没有经过缓存这⼀层。举个例⼦&#xff1a;某个⿊客故意制造我们缓存中不存在的 key 发起⼤量 请求&#xff0c;导致⼤量请求落到数据库。…

http笔记

文章目录1、什么是http&#xff1f;2、http报文格式3、请求报文1、认识URL2、认识http方法3、认识header4、响应报文5、https加密机制1、什么是http&#xff1f; http是应用层最广泛使用的协议之一&#xff1b;其中浏览器获取到网页就是基于http实现的&#xff1b;http就是浏览…

Caddy2学习笔记——Caddy2反向代理docker版本的DERP中继服务器

一、个人环境概述 本人拥有一个国内云服务商的云主机和一个备案好的域名&#xff0c;通过caddy2来作为web服务器。我的云主机系统是Ubuntu。 我的云主机是公网ip&#xff0c;地址为&#xff1a;43.126.100.78&#xff1b;我备案好的域名是&#xff1a;hotgirl.com。后面的文章…

【量化交易笔记】3.实现数据库保存数据

上一节&#xff0c;我们通过下载相关的 pandas 数据保存为 本地csv文件&#xff0c;这一节将上节的数据以数据库方式保存。 数据库保存 采集数据部分前一节已做说明&#xff0c;这里就直接用采用前面的内容。这里着重说明的事数据库连接。对与 python 相连接的数据库有很多&a…

玩转Python的交互(命令行)模式

我喜欢使用Python的交互界面&#xff08;命令行模式&#xff09;来运行和调试Python代码。为什么不用PyCharm、VSCode&#xff1f;因为先入为主&#xff0c;加上我的DOS命令行的情结&#xff0c;我第一次安装使用Python就是用这种黑白界面的&#xff0c;平时写代码惯用EmEditor…

MySQL慢查询

2 慢查询 2.1 慢查询介绍 MySQL的慢查询日志是MySQL提供的一种日志记录&#xff0c;它用来记录在MySQL中响应时间超过阀值的语句&#xff0c;具体指运行时间超过long_query_time值的SQL&#xff0c;则会被记录到慢查询日志中。具体指运行时间超过long_query_time值的SQL&…

软件测试之快速熟悉项目

快速熟悉项目 1、了解项目架构 C/S架构 C/S 代表的是客户端/服务器&#xff08;client/server&#xff09;&#xff0c;这类软件的使用者需要在本地电脑安装客户端程序&#xff0c;例如&#xff1a;QQ。 优点:安全性高。 缺点:一旦软件有更新&#xff0c;用户需要手动下载&am…

Rust 开发系列PyO3:Rust与Python的联动编程(中)

第三节&#xff1a;对比C语言的Python原生扩展开发模式 C/c编写Python扩展的方法&#xff0c;与Rust大致是相同的&#xff0c;如果不论语言本身的语法带来的繁琐的话&#xff0c;就单纯以开发步骤和模式来看&#xff0c;原生语言写扩展的步骤更为标准和简单。 大致来说&#…

QT入门Item Views之QTreeView

目录 一、QTreeView界面相关 1、布局介绍 二、基本属性功能 1、设置单元格不能编辑 2、一次选中一个item 3、去掉鼠标移动到单元格上的虚线框 4、最后一列自适应 三、代码展示 1、创建模型&#xff0c;导入模型 2、 右键菜单栏 3、双…

深度学习模型训练工作汇报(3.8)

进行数据的初始整理的准备 主要是进行伪序列字典的设置&#xff0c;以及训练数据集的准备。 期间需要的一些问题包括在读取文件信息的时候&#xff0c;需要跳过文件的第一行或者前两行&#xff0c;如果使用循环判断的话&#xff0c;会多进行n次的运算&#xff0c;这是不划算的…

003+limou+HTML——(3)HTML列表

000、前言 列表是网页常见的一种数据排列方式&#xff0c;在HTMl中列表一共有三种&#xff1a;有序列表、无序列表、定义列表&#xff08;另外“目录列表dir”和“菜单列表menu”已经在HTML5中被废除了&#xff0c;现在都是使用无序列表ul来替代&#xff09; 001、有序列表&a…

C/C++指针与数组(一)

预备知识 1、数据的存储 2、基本内建类型 1&#xff09;类型的大小 C offers a flexible standard with some guaranteed minimum sizes, which it takes from C: A short integer is at least 16 bits wide.An int integer is at least as big as short.A long integer is a…

Spring Cloud学习笔记:基础知识

这是本人学习的总结&#xff0c;主要学习资料如下 马士兵教育 目录1、Spring Cloud 简介2、Eureka3、建立Spring Cloud项目3.1、启动Server3.1.1、dependency3.1.2、配置文件3.1.3、Server端启动代码3.2、启动Client3.2.1、dependency3.2.2、配置文件3.3.3、Client端启动代码3…

Go之入门(特性、变量、常量、数据类型)

一、Go语言特性 语法简单并发性。Go语言引入了协程goroutine&#xff0c;实现了并发编程内存分配。Go语言为了解决高并发下内存的分配和管理&#xff0c;选择了tcmalloc进行内存分配&#xff08;为了并发设计的高性能内存分配组件&#xff0c;使用cache为当前线程提供无锁分配…