【C++初阶】11. list的使用及模拟实现

news2024/11/16 13:55:32

1. list的介绍

  1. list是可以在常数范围内在任意位置进行插入和删除的序列式容器,并且该容器可以前后双向迭代。
  2. list的底层是双向链表结构,双向链表中每个元素存储在互不相关的独立节点中,在节点中通过指针指向其前一个元素和后一个元素。
  3. list与forward_list非常相似:最主要的不同在于forward_list是单链表,只能朝前迭代,已让其更简单高效。
  4. 与其他的序列式容器相比(array,vector,deque),list通常在任意位置进行插入、移除元素的执行效率更好。
  5. 与其他序列式容器相比,list和forward_list最大的缺陷是不支持任意位置的随机访问,比如:要访问list的第6个元素,必须从已知的位置(比如头部或者尾部)迭代到该位置,在这段位置上迭代需要线性的时间开销;list还需要一些额外的空间,以保存每个节点的相关联信息(对于存储类型较小元素的大list来说这可能是一个重要的因素)

2. list的使用

在此只介绍几个list的常见接口使用,具体查看list的文档介绍
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
list为啥要单独实现sort排序呢?为啥不采用算法库当中的sort(lt.begin(),lt.end()),通过传迭代器区间进行排序呢?
因为list的结点都是单独开辟的,算法库当中通过两个迭代器相减来计算的数据个数,但是list并不能通过迭代器相减确定元素个数(vector和sting都是可以的,因为他们的迭代器就是原生指针),所以list要单独实现sort功能
在这里插入图片描述

库中在实现list时,提供了一些功能性接口:排序/去重… 但是在实际运用中使用不多
在比较算法性能时,不要使用debug版本,而是采用release版本,因为debug版本下优化没到位,结果可能与实际中的不一致
在这里插入图片描述
所以,在日常生活中是不会使用链表来进行排序的,sort已经如此,其他接口更是少用

3. list的模拟实现(简易版本)

因为之间在数据结构阶段实现过带头双向循环链表,这里就不过多赘述功能(大致介绍一下即可)

3.1 结点list_node

template <class T>
	struct list_node
	{
		list_node* _prev;
		list_node* _next;
		T _data;

		// 结点初始化
		list_node(const T& x)
			:_next(nullptr)
			,_prev(nullptr)
			,_data(x)
		{}
	};

3.2 链表初始化

template<class T>
	class list
	{
		typedef list_node<T> node;
	public:
		list()
		{
			//node里的data是T类型  T()构造
			_head = new node(T());
			_head->_next = _head;
			_head->_prev = _head;
		}
	private:
		node* _head;
	};

链表中只有一个哨兵位头结点,无参构造(初始化),要使得_next和_prev都指向自己
(带头双向循环链表的自洽,这里不理解的可以去参考数据结构中最开始的链表初始化,思路是一致的)

3.3 push_back

void push_back(const T& x)
		{
			node* newnode = new node(x);
			node* tail = _head->_prev;
			// _head tail newnode
			_head->_prev = newnode;
			tail->_next = newnode;
			newnode->_next = _head;
			newnode->_prev = tail;
		}

创建出来的newnode新结点调用node(x) 使用node的构造函数来创建结点
(不再像数据结构中还需要调用BuyListNode接口malloc空间来初始化,代码如下所示)

LTNode* BuyListNode(LTDataType x)
{
	LTNode* node = malloc(sizeof(LTNode));
	if (node == NULL)
	{
		perror("malloc fail");
		exit(-1);
	}
	node->data = x;
	node->next = NULL;
	node->prev = NULL;
	return node;
}

3.4 迭代器 (重点)

迭代器是属于内嵌类型的,那什么是内嵌类型呢? – 定义在类里面的就叫做内嵌类型

C++的内嵌类型分为两种

    1. 定义在类里面的内部类,内部类是外部类的友元
    1. typedef定义的类型名
      编译器在查找类型时,默认都是去全局找,而对于内嵌类型定义在类内部,那么在全局当中是找不到的,所以对于内部类和typedef的数据使用都需要声明类域
      在这里插入图片描述
      在这里插入图片描述

3.5 类的封装

typedef __list_iterator<T> iterator;

3.5.1 类成员及构造

template <class T>
	struct __list_iterator
	{
		typedef list_node<T> node;
		node* _pnode;

		// 构造函数
		__list_iterator(node* p)
			:_pnode(p)
		{}
	};

既然原生指针不支持,那么就亲自上手写一个满足自身需求的封装类,首先我们的需求是要遍历链表,所以需要一个结点的指针负责指向结点 ( node* _pnode ), 怎么初始化指针呢? – 根据所传参数初始化即可

3.5.2 operator* 重载解引用

解引用想要获取当前指针_pnode所指向的数据

		//解引用 operator*
		T& operator*()
		{
			return _pnode->_data;
		}

3.5.3 operator++ 重载++

++想指向当前指针的next(下一结点)

__list_iterator<T>& operator++()
		{
			_pnode = _pnode->_next;
			return *this;
		}

返回值为啥要写成__list_iterator<T>&类型呢?
迭代器++后还是迭代器

3.5.4 operator!= 重载不等于

		bool operator!=(const __list_iterator<T>& it)
		{
			return _pnode != it._pnode;
		}

3.6 迭代器区间

		iterator begin()
		{
			//匿名对象
			return iterator(_head->_next);
		}

		iterator end()
		{
			//匿名对象
			//iterator it(_head);
			//return it;
			return iterator(_head);
		}

begin就是哨兵位的下一位(_head->_next) ,end就是哨兵位(_head) 因为是带头双向循环,而且[ begin(),end() ) 是左闭右开区间,访问不到end() 正好契合迭代器区间的要求_head->prev就是尾结点

到这里简易版本的list就实现了,测试接口
在这里插入图片描述

4. 深刻理解迭代器

4.1 从逻辑结构分析:

在这里插入图片描述
在这里插入图片描述
迭代器的价值是什么?
如果说我们不采用迭代器的方式(STL库)来实现这些数据结构(vector/list),我们在提供这些访问方式是需要暴露底层的实现细节,在使用时必须告诉使用者我们底层实现的方式。同样的使用者在使用的过程中很可能不小心对底层的逻辑结构进行更改导致出现问题(非常不安全)
STL的大佬就提出了迭代器的概念(STL库中六大组件之一)

  1. 将底层实现封装起来,不暴露底层的实现细节
  2. 提供统一的访问方式,降低使用成本

C++是怎么做到可以提供统一的方式来访问呢?
类的独特价值,对于内置类型解引用直接访问到数据,而对于自定义类型无法做到,那么我们就进行运算符重载(按照自己的想法实现),
这其中C++的引用的作用也不可替代,传引用返回更改对应的数据
对于C语言来说,没有类,没有模板,没有运算符重载,没有引用,是无法实现的。
其实就相当于迭代器帮我们承受了底层的实现细节,我们在上层调用时才能轻松/一致

4.2 从内存的角度分析:

list的迭代器其中只包含node* _pnode的指针,所占字节为4字节,尽管list迭代器当中包含大量的自定义实现的函数接口,但这些接口都不占用内存空间,所以归根结底 list迭代器占用内存空间大小和vector的原生指针一致
vector的迭代器就是T*类型的原生指针,所占字节为4字节
其中it = lt.begin() 时,发生了拷贝构造,又因为我们自身并未实现对迭代器的拷贝构造,所以编译器自动生成浅拷贝it和begin()所指向一个结点(符合要求)

4.3 通过调试来深刻理解:

在这里插入图片描述
在这里插入图片描述

5. 完善list功能

5.1 删除pos位置

		void erase(iterator pos)
		{
			// 不能将哨兵位的头结点删除
			assert(pos != end());

			node* prev = pos._pnode->_prev;
			node* next = pos._pnode->_next;

			prev->_next = next;
			next->_prev = prev;

			delete pos._pnode;
		}

但是这种删除无法有效解决迭代器失效的问题,所以需要记录迭代器的返回值

iterator erase(iterator pos)
{
	// 不能将哨兵位的头结点删除
	assert(pos != end());

	node* prev = pos._pnode->_prev;
	node* next = pos._pnode->_next;

	prev->_next = next;
	next->_prev = prev;

	delete pos._pnode;
	
	// 拿next来构造迭代器对象
	return iterator(next);
}

5.2 在pos位置前插入

// 在pos位置之前插入 newnode
iterator insert(iterator pos, const T& x)
{
	node* newnode = new node(x);
	node* cur = pos._pnode;
	node* prev = pos._pnode->_prev;

	prev->_next = newnode;
	newnode->_prev = prev;
	newnode->_next = cur;
	cur->_prev = newnode;
	
	// 更新迭代器
	return iterator(newnode);
}

5.3 复用接口(头/尾-插入/删除)

		// 尾插->也就是在end之前插入
		void push_back(const T& x)
		{
			insert(end(), x);
		}

		// 头插->在begin之前插入
		void push_front(const T& x)
		{
			erase(begin(),x);
		}
		// 头删 删除begin()位置
		void pop_front()
		{
			erase(begin());
		}
		// 尾删 删除end前位置
		void pop_back()
		{
			erase(--end());
		}

5.4 析构函数

		~list()
		{
			clear();

			delete _phead;
			_phead = nullptr;
		}
		void clear()
		{
			iterator it = lt.begin();
			if (it != end())
			{
				// 这边erase过后迭代器不能++,因为迭代器已经失效了
				// 但是我们记录下的erase过后迭代器的位置,所以用it = 
				it = erase(it);
			}
		}

5.5 拷贝构造(传统写法)

        // 模拟stl库实现一下封装
		void empty_initialize()
		{
			_head = new node(T());
			_head->_next = _head;
			_head->_prev = _head;
		}
		// 拷贝构造
		list(const list<T>& lt)
		{
			empty_initialize();

			for (const auto& e : lt)
			{
				push_back(e);
			}
		}

传统写法的深拷贝 – 复用接口
先构造出带头双向循环的初结构,不断的进行push_back()

5.6 赋值重载(传统写法)

		// lt1 = lt3
		list<T>& operator=(const list<T>& lt)
		{
			if (this != &lt)
			{
				clear(); // (this->)clear();
				for (const auto& e : lt)
				{
					push_back(e); // (this->)push_back(e);
				}
			}
			return *this;
		}

lt1(this) 当中是存在数据的,那么首先需要将lt1当中的数据清空 再不断的push_back尾插数据
其中const auto& e是重点 不采用引用取别名的方式,e是需要拷贝构造出来的临时对象

5.7 拷贝构造(现代写法)

在这里插入图片描述

5.8 赋值重载(现代写法) – 推荐

		// 拷贝构造 -- 现代写法
		// 参数一定不能传引用 
		list<T>& operator=(const list<T> lt)
		{
			// 不传引用 lt就是拷贝构造的临时对象
			// 二者交换以后,都正常析构
			swap(lt);
			return *this;
		}

调用拷贝构造创建出临时对象并不会影响lt本身,所以二者的交换也就是跟临时对象的交换,这种写法明显优于传统写法
传统写法是先清空数据再不断尾插,而现代写法是让编译器拷贝构造个临时对象再交换数据

6. const 迭代器(重点)

	void print_list(const list<int>& lt)
	{
		const list<int>:: iterator cit = lt.begin();
	}

提问:在普通迭代器前加const进行修饰是否就是const迭代器?
在这里插入图片描述
要实现const迭代器那么就是解引用返回const类型即可,解引用就无法更改数据(达到const迭代器的需求)
在这里插入图片描述
但是我们无法这样实现,因为迭代器对象就是被const所修饰无法进行++操作,只能解引用
除非我们对operator++也进行重载专门重载成const T& operator++() const
但是operator++是需要修改迭代器的,所以无法实现对operator++的重载

那么真正的const迭代器该如何实现呢?

6.1 简易版本

直接创建一个const版本的迭代器(跟正常的迭代器区分开)

	// 跟普通迭代器的区别
	// 可以遍历 但是不能解引用修改 *it
	template <class T>
	struct __list_const_iterator
	{
		typedef list_node<T> node;
		node* _pnode;
		// 构造函数
		__list_const_iterator(node* p)
			:_pnode(p)
		{}

		// 解引用 operator*
		// 返回const T& 
		const T& operator*()
		{
			return _pnode->_data;
		}

		__list_const_iterator<T>& operator++()
		{
			_pnode = _pnode->_next;
			return *this;
		}

		__list_const_iterator<T>& operator--()
		{
			_pnode = _pnode->_prev;
			return *this;
		}

		bool operator!=(const __list_const_iterator<T>& it)
		{
			return _pnode != it._pnode;
		}
	};

唯一的区别就是operator* 返回的是 const T&
operator++ 还是用的普通迭代器的方式

6.2 大佬版本(模板的力量)

STL库是由大佬实现的,大佬是不会允许代码出现如此冗余的现象,迭代器和const迭代器的区别就在类名和operator*的返回值上,其它完全一致

对于vector<int> / vector<char> / vector<string> 会根据类模板实例化出三个不同的类型

// 模板的力量:
	// 增加1个模板参数
	// typedef __list_iterator<T, T&> iterator;
	// typedef __list_iterator<T, const T&> const_iterator;
	template <class T, class Ret>
	struct __list_iterator
	{
		typedef list_node<T> node;

		//typedef的力量 
		typedef __list_iterator<T, Ret> Self;

		node* _pnode;

		// 构造函数
		__list_iterator(node* p)
			:_pnode(p)
		{}

		// 解引用 operator*
		Ret operator*()
		{
			return _pnode->_data;
		}

		// iterator it
		// *it
		// ++it;
		//const T& operator*() const 
		//{
		//	return _pnode->_data;
		//}

		Self& operator++()
		{
			_pnode = _pnode->_next;
			return *this;
		}

		Self& operator--()
		{
			_pnode = _pnode->_prev;
			return *this;
		}

		bool operator!=(const Self& it)
		{
			return _pnode != it._pnode;
		}
	};

这种大佬写的版本本质就是利用类模板会根据参数的不同实例化出不同类型的对象
增加一个Ret参数,根据传入的参数是T&还是const T&生成不同的迭代器
本质上跟我们自己实现两个迭代器没有区别,只是这个操作交给编译器(实例化两个对象) – 代码复用
这也是typedef的力量 typedef __list_iterator<T, Ret> Self; 一个简简单单的typedef就可以实现两个类型
对于类名和类型的区别:
普通类 类名等价于类型
类模板 类名不等价于类型
比如:list模板当中 类名叫list 而类型list
类模板中可以用类名来代替类型,但是不建议这样用

		// lt2(lt1)
		// list(const list<T>& lt) --推荐
		list(const list& lt) // 不建议
		{
			empty_initialize();

			list<T> tmp(lt.begin(), lt.end());
			swap(tmp);
		}

		// lt3 = lt1
		// list<T>& operator=(list lt) --推荐
		list& operator=(list lt) // 不建议
		{
			swap(lt);
			return *this;
		}

虽然说在类内部这样写不会报错,但说到底这种写法不规范并没有真正意义上的将类型名作为参数 而是使用的类名
所以不推荐,但是需要说明一点在类外使用模板实例化对象必须说明类型不然会报错滴!!!

	// list lt; -- 报错
	list<int> lt;

7. 完善迭代器功能

7.1 operator–(单参模板)

		__list_iterator<T>& operator--()
		{
			_pnode = _pnode->_prev;
			return *this;
		}

但是此时出现了两个模板参数,所实现的类型名就分两种(__list_iterator<T>__list_const_iterator<T>) 该怎么办呢?
还是运用模板和typedef的力量

7.2 多参模板

		// 前置++ 用传引用返回
		Self& operator++()
		{
			_pnode = _pnode->_next;
			return *this;
		}
		// 后置++ 用传值返回
		Self operator++(int)
		{
			Self tmp(*this);
			_pnode = _pnode->_next;
			return tmp;
		}

		// 前置-- 用传引用返回
		Self& operator--()
		{
			_pnode = _pnode->_prev;
			return *this;
		}
		// 后置-- 用传值返回
		Self operator--(int)
		{
			// 先拷贝出tmp对象
			Self tmp(*this);
			// 再对this对象--
			_pnode = _pnode->_prev;
			// 返回--前的tmp对象
			return tmp;
		}

通过这里的代码也可看出如果前置或者后置都可以时,尽量选择前置操作,因为前置采用的是传引用返回,而后置采用传值返回
后置相较于前置多了两次拷贝构造 tmp – 1次,返回值 – 2次
所以前置的性能更好

		bool operator!=(const Self& it)
		{
			return _pnode != it._pnode;
		}

operator != 也用到了typedef Self

7.3 operator->(重载->)

在这里插入图片描述
在这里插入图片描述

		// 只适用于普通迭代器
		T* operator->()
		{
			return &_pnode->_data;
		}

为了契合const迭代器,需要在模板参数当中再添加一个值(因为const迭代器->指向的数据也不能被修改)

template <class T, class Ret,class Ptr>
	struct __list_iterator
	{
		typedef list_node<T> node;

		//typedef的力量
		typedef __list_iterator<T, Ret, Ptr> Self;
		// 解引用 operator*
		Ret operator*()
		{
			return _pnode->_data;
		}
		Ptr operator->()
		{
			return &_pnode->_data;
		}
		// .....
	}
		typedef __list_iterator<T, T&,T*> iterator;
		typedef __list_iterator<T, const T&,const T*> const_iterator;

8. 总结:

vector和list的区别

在这里插入图片描述

迭代器失效问题

vector不管是插入还是删除都会出现迭代器失效的问题
插入可能扩容造成迭代器失效
删除越界访问造成迭代器失效
list是删除会出现迭代器失效的问题
删除越界访问造成迭代器失效
为啥在string当中不谈迭代器失效的问题呢?
因为string对象都是使用的pos(下标)来进行操作,通常不使用迭代器
在这里插入图片描述
同样的string也存在迭代器失效的问题(跟vector类似)

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

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

相关文章

清华青年AI自强作业hw5:基于CNN实现CIFAR10分类任务

清华青年AI自强作业hw5&#xff1a;基于CNN实现CIFAR10分类任务 简述作业实现遇到的问题相关链接 一起学AI系列博客&#xff1a;目录索引 简述 hw5作业为利用深度卷积神经网络实现CIFAR_10数据集十分类问题&#xff0c;帮助理解CNN的前向传播结构。 CIFAR-10是一个常用的彩色图…

现代处理器结构

本文翻译自&#xff1a;Modern Microprocessors A 90-Minute Guide!&#xff0c;&#xff0c;我认为原文是相当好的计算机体系结构方面的概述&#xff0c;与时代相结合是国内计算机课本普遍缺失的一环&#xff0c;本文可作为一个有效的补充&#xff0c;向原作者和其他译者表示感…

青岛大学_王卓老师【数据结构与算法】Week03_09_线性表的链式表示和实现9_学习笔记

本文是个人学习笔记&#xff0c;素材来自青岛大学王卓老师的教学视频。 一方面用于学习记录与分享&#xff0c;另一方面是想让更多的人看到这么好的《数据结构与算法》的学习视频。 如有侵权&#xff0c;请留言作删文处理。 课程视频链接&#xff1a; 数据结构与算法基础–…

软件测试面试-银行篇

今天参加了一场比较正式的面试&#xff0c;汇丰银行的视频面试。在这里把面试的流程记录一下&#xff0c;结果还不确定&#xff0c;但是面试也是自我学习和成长的过程&#xff0c;所以记录下来大家也可以互相探讨一下。 请你做一下自我介绍&#xff1f;&#xff08;汇丰要求英…

如何在VUE项目中使用svg图标

一文带你搞定svg图标的使用&#xff01; 文章目录 前言一、SVG相较于字体图标的优点二、使用步骤1.新建一个vue2项目2.安装项目依赖3 .在src目录下新建文件夹4.创建svg-icon组件5.在main.js中引入icons下的index.js6.配置 vue.config.js7.步骤完毕&#xff0c;检验成果 总结 前…

LoadRunner负载均衡与IP欺骗

目录 前言&#xff1a; 一、IP wizard 二、一个IP欺骗测试脚本 三、Controller与Runtime setting设置 四、mdrv.dat配置调整 五、查看实现效果 【结语】 前言&#xff1a; 在使用 LoadRunner 进行负载测试时&#xff0c;负载均衡和 IP 欺骗是两个重要的概念。 这次出差…

【UE Unreal Camera】【保姆级教程】手把手教你通过UE打开摄像头/预览图像画面

【UE Unreal Camera】【保姆级教程】手把手教你通过UE打开摄像头/预览图像画面 概述 最近在做一个游戏的开发&#xff0c;需要通过UE去打开用户的摄像头&#xff08;ios,android上的手机摄像头&#xff1b;windows,mac上的电脑摄像头&#xff09;&#xff0c;预览图像&#xf…

NLP(五十七)LangChain使用Google Search Agent

大模型存在的问题 大模型在给人带来眼前一亮的表现&#xff0c;深深地震撼各行各业人们的同时&#xff0c;其本身也存在着不少问题。   以OpenAI的ChatGPT模型为例&#xff0c;其存在的问题有&#xff1a; 事实错误&#xff0c;容易一本正经地胡说八道&#xff0c;造成幻觉问…

重定向爬虫和多线程爬虫

前言 重定向爬虫是指在抓取网页时&#xff0c;如果目标网站内部存在重定向机制&#xff0c;即当你访问一个网页时&#xff0c;服务器会把你重定向到另一个目标网页。重定向爬虫可以帮助我们发现这种重定向链接&#xff0c;从而更有效地抓取目标网站的内容。 要实现重定向爬虫…

分享一些关于 CSS Grid 基础入门知识

网格系统&#xff08;CSS Grid&#xff09;是CSS中最重要的特性之一。它能够以简单的方式将元素对齐到列和行中。CSS网格使得设计复杂且响应式的网页变得更加容易&#xff0c;无需使用浮动、表格或定位。它还具有许多更强大的功能&#xff0c;如果你多加练习&#xff0c;就能发…

北京大学2018计算机学科夏令营上机考试

目录 A:计算两个日期之间的天数【暴力不水】 B:回文子串【暴力不水】 C:The Die Is Cast【DFS】 D:Euro Efficiency【看不懂】 E:重要逆序对【归并排序】 F:Tram【看不懂】 G:食物链【图】 H:DFS spanning tree【不会】 A:计算两个日期之间的天数【暴力不水】 //…

《微服务架构设计模式》第四章 使用Saga管理事务

内容总结自《微服务架构设计模式》 使用Saga管理事务 一、XA解决方案存在问题二、使用Saga管理事务Saga是什么补偿事务是什么Saga协调模式协同式Saga编排式Saga 隔离性Saga结构 三、总结 一、XA解决方案存在问题 在多个服务、数据库和消息代理之间维持数据一致性的传统方式是采…

小黑重庆归来,眼睛复查顺利,见到了三年没见的线上同门的leetcode之旅:剑指 Offer II 015. 字符串中的所有变位词

小黑代码1:滑动窗口 class Solution:def findAnagrams(self, s: str, p: str) -> List[int]:# 字符串长度n_s len(s)n_p len(p)if n_s < n_p:return []# 差值数组arr [0] * 26# 初始窗口for i in range(n_p):arr[ord(p[i])-97] - 1arr[ord(s[i])-97] 1# 计算初始窗口…

Web服务器群集:Tomcat配置https证书

目录 一、理论 1.SSL 2.HTTPS协议和HTTP协议的区别 3.https证书配置 4.tomcat强制使用https 二、实验 1.https证书配置过程 2.tomcat强制使用https 三、总结 一、理论 1.SSL &#xff08;1&#xff09;概念 SSL是网络加密传输协议&#xff0c;是支持在网络服务器(主…

chatgpt赋能python:烧录代码过程是怎样的

烧录代码过程是怎样的 烧录代码是将编写好的程序代码烧录进内置闪存器件&#xff08;Flash&#xff09;或外部存储器&#xff08;SD卡、EEPROM等&#xff09;中的过程。本文将介绍烧录代码的具体过程和常用工具&#xff0c;以及一些注意事项。 烧录代码的步骤 步骤一&#x…

分布式负载均衡 Ribbon

一、Ribbon简介 是Netfix发布的负载均衡&#xff0c;Eureka一般配合Ribbon进行使用&#xff0c;基于HTTP和TCP的客户端负载均衡工具。 只有负载均衡的能力&#xff0c;不具有发送请求的能力&#xff0c;要配合服务通信组件。 RestTemplate 针对各种类型的 HTTP 请求都提供了相…

青少年机器人技术一级考试备考重点(一):机器人常识

随着机器人技术的飞速发展&#xff0c;越来越多的青少年开始关注并参与其中。青少年机器人技术考试作为一项评估学生机器人技术水平的重要考试&#xff0c;备受广大青少年和家长的关注。为了更好地备战青少年机器人技术一级考试&#xff0c;了解考试的学习要点和备考重点是非常…

LeetCode-67. 二进制求和

LeetCode-67. 二进制求和 1、题目描述2、解题思路3、代码实现4、解题记录 ) 1、题目描述 题目描述&#xff1a; 给你两个二进制字符串 a 和 b &#xff0c;以二进制字符串的形式返回它们的和。 示例1&#xff1a; 输入:a “11”, b “1” 输出&#xff1a;“100” 示例2&…

kafka实现消息接受和发送

1、首先引入依赖 <dependency><groupId>org.springframework.kafka</groupId><artifactId>spring-kafka</artifactId> </dependency> <dependency><groupId>org.springframework.kafka</groupId><artifactId>spr…

第九十五天学习记录:C++核心:类和对象Ⅳ(五星重要)

C对象模型和this指针 成员变量和成员函数分开存储 在C中&#xff0c;类内的成员变量和成员函数分开存储只有非静态成员变量才属于类的对象上 #include<iostream> using namespace std;class Person {int m_A;//非静态成员变量 属于类的对象上static int m_B;//静态成…