【C++初阶】11,2024年最新掌握这套精编大数据开发高级面试题解析

先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7

深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新大数据全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上大数据知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以添加V获取:vip204888 (备注大数据)
img

正文

		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;
		}
		// .....
	}



**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**

**需要这份系统化的资料的朋友,可以添加V获取:vip204888 (备注大数据)**
![img](https://img-blog.csdnimg.cn/img_convert/cfd61a94225ef2df90ed507f9ac4e781.png)

**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**

erator
	{
		typedef list_node<T> node;

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



**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**

**需要这份系统化的资料的朋友,可以添加V获取:vip204888 (备注大数据)**
[外链图片转存中...(img-KU3xtQHT-1713338919327)]

**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值