【C++】list的模拟实现

news2024/12/25 13:25:29

文章目录

    • 1.list 底层
    • 2. list的模拟实现
      • 1. list_node 类设计
      • 2. list类如何调用类型
      • 3 .push_back(正常实现)
      • 4. 迭代器的实现
        • 第一个模板参数T
        • const迭代器
        • 第二个模板参数Ref
        • 第三个模板参数Ptr
        • 对list封装的理解
      • 5. insert
      • 6.push_back与 push_front(复用)
      • 7. erase
      • 8. pop_back与pop_front (复用)
      • 9. clear 清空数据
      • 10. 迭代器区间构造
      • 12. 拷贝构造
        • 传统写法
        • 现代写法
      • 13. 赋值
    • 3.完整代码

1.list 底层

list为任意位置插入删除的容器,底层为带头双向循环链表

begin() 代表第一个结点,end()代表最后一个结点的下一个

2. list的模拟实现

1. list_node 类设计

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

	};

C++中,Listnode作为类名,而next和prev都是类指针,指针引用成员时使用->,而对象引用成员时使用 .
通过显示实例化,将两个类指针指定类型为T


2. list类如何调用类型

Listnode 代表类型
Listnode 代表类名 + 模板参数 才是类型
而_head 以及创建新节点前都需加上类型

3 .push_back(正常实现)

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

当我们想要创建一个节点时,需要调用node的构造函数
typedef list_node node ,node是由 list_node 类提供的


list_node(const T& x=T())//list类的构造函数
			:_next(nullptr)
			, _prev(nullptr)
			, _data(x)
		{
		}

最好在构造函数处提供全缺省,对于内置类型int可以使用0,但对于自定义类型来说就不可以,所以为了满足泛型的要求,使用匿名对象调用默认构造函数

4. 迭代器的实现

若在数组上有一个int类型的指针,解引用是int类型的数据,再++可以加载下一个位置,因为物理空间是连续的


同样若在链表上,解引用类型为 node,再++不能到下一个节点,因为物理空间不连续
所以构造迭代器通过封装节点的指针来进行构造 (后面会讲)

第一个模板参数T

创建一个_list_iterator的类,来实现迭代器功能


template<class T>
    struct _list_iterator
    {
        typedef list_node<T> node;
        typedef _list_iterator<T> self;
        node* _node;
        
        _list_iterator(node* n)
            :_node(n)
        {

        }
        T& operator*()//解引用 
        {
            return _node->_data;
        }
        _list_iterator<T>& operator++()//返回迭代器
        {
            _node = _node->_next;//指向下一个节点
            return *this;
        }
        bool operator!=(const self&s)
        {
            return _node != s._node;
        }
    };

在list类中,调用迭代器实现begin()和end()功能
typedef _list_iterator<T,T&,T*> iterator,
通过typedef 将_list_node类模板定义为iterator


iterator begin()
		{
			return iterator(_head->_next);//通过匿名对象访问第一个数据
		}
		iterator end()
		{
			return iterator(_head);//通过匿名对象访问最后一个数据的下一个
		}

在list类中实现begin()和end(),内部调用_list_node类的构造函数


在这里插入图片描述

const迭代器

在这里插入图片描述

假设第一个代表的是T * ,而第二个代表的 T * const,保护迭代器本身不可以被修改,而我们想要保护迭代器指向的内容不可被修改即 const T*


复制_list_iterator类中的内容,并将名字修改为const_list_iterator
只需修改*operator类型为cosnt T& ,说明解引用后的数据返回不能被修改


template<class T>
    struct _list_const_iterator
    {
        typedef list_node<T> node;
        typedef _list_const_iterator<T> self;
        node* _node;

        _list_const_iterator(node* n)
            :_node(n)
        {

        }
        const T& operator*()//解引用 
        {
            return _node->_data;
        }
        self& operator++()//前置++
        {
            _node = _node->_next;//指向下一个节点
            return *this;
        }
        self& operator++(int)//后置++
        {
            self ret = *this;
            _node = _node->_next;
            return ret;
        }
        self& operator--()//前置--
        {
            _node = _node->_prev;
            return *this;
        }
        self& operator--(int)//后置--
        {
            self ret = *this;
            _node = _node->_prev;
            return ret;
        }
        bool operator!=(const self& s)//!=
        {
            return _node != s._node;
        }
        bool operator==(const self& s)//==
        {
            return _node == s._node;
        }
    };

在这里插入图片描述

第二个模板参数Ref

迭代器和const迭代器只有 *operator 的返回值不同,
只需在模板中添加一个参数即可使用一个迭代器实现迭代器和const 迭代器的功能

在这里插入图片描述


在这里插入图片描述

第三个模板参数Ptr

对于内置类型int使用解引用找到对应数据,而自定义类型需使用->寻找下一个节点


AA作为自定义类型,想要找到下一个节点需要使用->,在迭代器中 重载 - >

it->_a1,实际上是 it->->_a1,
it->返回值为AA* ,再通过这个指针使用->指向_a1
但是为了增强可读性,省略了一个->
it->_a1 实际上为 it.operator->()->._a1


在这里插入图片描述


在这里插入图片描述

对list封装的理解

在不考虑封装的情况下,两者等价


从物理空间上来看,it与pnode都是指向1的地址



pnode作为一个原生指针,解引用指针就会拿到这个地址,找到这个地址指向空间的内容
++pnode,找到下一个节点的地址,但是下一个节点不一定是要的节点
*it 识别成为自定义类型就会调用函数

5. insert

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

6.push_back与 push_front(复用)

两者都可以通过复用 insert的方式实现

void push_back(const T&x)//尾插
        {
            /*node* tail = _head->_prev;
            node* newnode = new node(x);
            tail->_next = newnode;
            newnode->_prev = tail;
            newnode->_next = _head;
            _head->_prev = newnode;*/
            insert(end(), x);
        }
        void push_front(const T&x)//头插
        {
            insert(begin(), x);
        }
        

7. erase

void erase(iterator pos)//删除pos位置
        {
            //头节点不可以删除
            assert(pos != end());
            node* cur = pos._node;
            node* prev = cur->_prev;
            node* next = cur->_next;
            prev->_next = next;
            next->_prev = prev;
            delete cur;
        }

由于头节点不可以删除,所以使用assert断言的方式

8. pop_back与pop_front (复用)

                void pop_back()//尾删
        {
            erase(--end());
        }
        void pop_front()//头删
        {
            erase(begin());
        }

9. clear 清空数据

void clear()//清空数据
            //但是要注意不要把head清掉
        {

            iterator it = begin();
            while (it != end())
            {
                it=erase(it);//为了防止迭代器失效设置返回值
                 //返回值为当前节点的下一个
            }
        }

迭代器失效是指迭代器所指向的节点失效 即节点被删除了
erase函数执行后,it所指向的节点被删除,因此it无效,在下一次使用it时,必须先给it赋值



为了防止迭代器失效所以使erase设置返回值,返回值为当前节点的下一个

10. 迭代器区间构造

void empty_init()
		{
			_head = new node;
			_head->_next = _head;
			_head->_prev = _head;
		}
template <class Iterator>
		list(Iterator first, Iterator last)
		{
			empty_init();
			while (first != last)
			{
				push_back(*first);
				++first;
			}
		}

想要尾插的前提时,需要有头节点的存在,所以设置一个函数对头节点初始化


12. 拷贝构造

传统写法

void empty_init()
		{
			_head = new node;
			_head->_next = _head;
			_head->_prev = _head;
		}
list(const list<T>& It)//拷贝构造  传统写法
        {
            empty_init();//初始化头节点
            for (auto e : It)
            {
                push_back(e);
            }
        }

现代写法

void swap(list<T>& tmp)
        {
            std::swap(_head, tmp._head);
        }
        list(const list<T>& It)//拷贝构造现代写法
        {
            empty_init();//将头节点初始化
            list<T> tmp(It.begin(), It.end());
            swap(tmp);
        }

通过调用std中的swap来达到交换的目的

13. 赋值

void swap(list<T>& tmp)
		{
			std::swap(_head, tmp._head);
		}
list<T>& operator=(list<T> It)
        {
            swap(It);
            return *this;
        }

参数不可以使用 list &,虽然可以达到赋值的目的,但是It的值也会发生改变


在这里插入图片描述


在这里插入图片描述

3.完整代码

#include<iostream>
#include<list>
#include<assert.h>
using namespace std;
namespace yzq
{
	template<class T>
	struct list_node
	{
		list_node<T>* _next;
		list_node<T>* _prev;
		T _data;

		list_node(const T& x=T())//构造函数
			:_next(nullptr)
			, _prev(nullptr)
			, _data(x)
		{
		}
	};
	//迭代器
	template<class T,class Ref,class Ptr>
	struct _list_iterator
	{
		typedef list_node<T> node;
		typedef _list_iterator<T,Ref,Ptr> self;
		node* _node;
		_list_iterator(node* n)
			:_node(n)
		{

		}
		Ref operator*()//解引用 
		{
			return _node->_data;
		}
		Ptr operator->()//->
		{
			return &_node->_data;
		}
		self& operator++()//前置++
		{
			_node = _node->_next;//指向下一个节点
			return *this;
		}
		self& operator++(int)//后置++
		{
			self ret = *this;
			_node = _node->_next;
			return ret;
		}
		self& operator--()//前置--
		{
			_node = _node->_prev;
			return *this;
		}
		self& operator--(int)//后置--
		{
			self ret = *this;
			_node = _node->_prev;
			return ret;
		}
		bool operator!=(const self&s)//!=
		{
			return _node != s._node;
		}
		bool operator==(const self& s)//==
		{
			return _node == s._node;
		}
	};
	//const迭代器
	//template<class T>
	//struct _list_const_iterator
	//{
	//	typedef list_node<T> node;
	//	typedef _list_const_iterator<T> self;
	//	node* _node;

	//	_list_const_iterator(node* n)
	//		:_node(n)
	//	{

	//	}
	//	const T& operator*()//解引用 
	//	{
	//		return _node->_data;
	//	}
	//	self& operator++()//前置++
	//	{
	//		_node = _node->_next;//指向下一个节点
	//		return *this;
	//	}
	//	self& operator++(int)//后置++
	//	{
	//		self ret = *this;
	//		_node = _node->_next;
	//		return ret;
	//	}
	//	self& operator--()//前置--
	//	{
	//		_node = _node->_prev;
	//		return *this;
	//	}
	//	self& operator--(int)//后置--
	//	{
	//		self ret = *this;
	//		_node = _node->_prev;
	//		return ret;
	//	}
	//	bool operator!=(const self& s)//!=
	//	{
	//		return _node != s._node;
	//	}
	//	bool operator==(const self& s)//==
	//	{
	//		return _node == s._node;
	//	}
	//};

	template <class T>
	class list
	{
		typedef list_node<T> node;
	public:
		typedef _list_iterator<T,T&,T*> iterator;
		typedef _list_iterator<T, const T&, const T*> const_iterator;//const迭代器
		//typedef _list_const_iterator<T> const_iterator;
		void empty_init()
		{
			_head = new node;
			_head->_next = _head;
			_head->_prev = _head;
		}
		list()//构造函数
		{
			empty_init();
		}
		template <class Iterator>
		list(Iterator first, Iterator last)
		{
			empty_init();
			while (first != last)
			{
				push_back(*first);
				++first;
			}
		}
		//list(const list<T>& It)//拷贝构造
		//{
		//	empty_init();//初始化头节点
		//	for (auto e : It)
		//	{
		//		push_back(e);
		//	}
		//}
		void swap(list<T>& tmp)
		{
			std::swap(_head, tmp._head);
		}
		list(const list<T>& It)//拷贝构造现代写法
		{
			empty_init();//将头节点初始化
			list<T> tmp(It.begin(), It.end());
			swap(tmp);
		}
		list<T>& operator=(list<T> It)//赋值
		{
			swap(It);
			return *this;
		}
		~list()//析构函数
		{
			//将头节点也要释放掉
			clear();
			delete _head;
			_head = nullptr;
		}
		void clear()//清空数据
			//但是要注意不要把head清掉
		{

			iterator it = begin();
			while (it != end())
			{
				it=erase(it);//为了防止迭代器失效设置返回值
				 //返回值为当前节点的下一个
			}
		}

		
		void push_back(const T&x)//尾插
		{
			/*node* tail = _head->_prev;
			node* newnode = new node(x);
			tail->_next = newnode;
			newnode->_prev = tail;
			newnode->_next = _head;
			_head->_prev = newnode;*/
			insert(end(), x);
		}
		void push_front(const T&x)//头插
		{
			insert(begin(), x);
		}
		void insert(iterator pos,const T&x)//在pos位置前插入x
		{
			node* cur = pos._node;
			node* prev = cur->_prev;
			node* newnode = new node(x);
			prev->_next = newnode;
			newnode->_prev = prev;
			newnode->_next = cur;
			cur->_prev = newnode;
		}
		iterator erase(iterator pos)//删除pos位置
		{
			//头节点不可以删除
			assert(pos != end());
			node* cur = pos._node;
			node* prev = cur->_prev;
			node* next = cur->_next;
			prev->_next = next;
			next->_prev = prev;
			delete cur;
			return iterator(next);
		}
		void pop_back()//尾删
		{
			erase(--end());
		}
		void pop_front()//头删
		{
			erase(begin());
		}
		iterator begin()
		{
			return iterator(_head->_next);//通过匿名对象访问第一个数据
		}
		iterator end()
		{
			return iterator(_head);//通过匿名对象访问最后一个数据的下一个
		}
		const_iterator begin()const
		{
			return const_iterator(_head->_next);
		}
		const_iterator end()const
		{
			return const_iterator(_head);
		}
	private:
		node* _head;
	};
	
	/*void test(const list<int>&e)
	{
		list<int>::const_iterator it = e.begin();
			while (it != e.end())
			{
					cout << *it << " ";
				++it;
			}
			cout << endl;
	}
	void test2()
	{
		const list<int> v;
		test(v);

	}*/
	//void test1()
	//{
	//	list<int> v;
	//	v.push_back(1);
	//	v.push_back(2);
	//	v.push_back(3);
	//	v.push_back(4);
	//	list<int>::iterator it= v.begin();
	//	while (it != v.end())
	//	{
	//		cout << *it << " ";
	//		++it;
	//	}
	//	cout << endl;
	//}
	/*struct AA
	{
		int	_a1;
		int _a2;
		AA(int a1=0,int a2=0)
			:_a1(a1)
			,_a2(a2)
		{
		}
	};*/
	/*void test3()
	{
		list<AA>v;
		v.push_back(AA(1, 1));
		v.push_back(AA(2, 2));
		v.push_back(AA(3, 3));
		list<AA>::iterator it = v.begin();
		while (it != v.end())
		{
			cout << it->_a1 << " :"<<it->_a2<<" ";
			cout << endl;
			it++;
		}
	}*/
	//void test4()
	//{
	//	list<int> v;
	//	v.push_back(1);
	//	v.push_back(2);
	//	v.push_back(3);
	//	v.push_back(4);// 1 2 3 4
	//	for (auto e : v)
	//	{
	//		cout << e << " ";
	//	}
	//	cout << endl;
	//	v.clear();
	//	v.push_back(4);//  4
	//	for (auto e : v)
	//	{
	//		cout << e << " ";
	//	}
	//}
	//void test4()
	//{
	//	list<int> v;
	//	v.push_back(1);
	//	v.push_back(2);
	//	v.push_back(3);
	//	v.push_back(4);// 1 2 3 4
	//	for (auto e : v)
	//	{
	//		cout << e << " ";
	//	}
	//	cout << endl;
	//	list<int> v2(v);
	//	for (auto e : v2)// 1 2 3 4
	//	{
	//		cout << e << " ";
	//	}
	//}
	void test4()
	{
		list<int> v;
		v.push_back(1);
		v.push_back(2);//v1 = 1 2
		list<int> v2;
		v2.push_back(5);
		v2.push_back(6);//v2=5 6
			v2 = v;
		for (auto e : v2)// v2=1 2 
		{
			cout << e << " ";
		}
		cout << endl;
		for (auto e : v)// v1=1 2
		{
			cout << e << " ";
		}
	}
}	

int main()
{
	yzq::test4();
	return 0;
}

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

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

相关文章

[c++]list模拟实现

目录 前言&#xff1a; 学习类的方式&#xff1a; 1 类成员变量 1.1 list成员变量 1.2 结点结构体变量 1.3 迭代器成员变量 2 默认函数——构造 2.1 结点结构体构造函数 2.2 list构造函数 2.3 迭代器构造函数 3 迭代器实现 3.1 list部分 3.2 迭代器结构体部分 3.2…

Spring MVC程序开发(三大功能)

文章目录一、什么是Spring MVC?1.MVC定义2.MVC与Spring MVC的关系3.创建方式二、Spring MVC的核心功能1.连接功能浏览器获取前端接口和后端程序连接功能实现get和post的区别Spring Boot热部署2.获取参数&#xff08;1&#xff09;传递单个参数&#xff08;2&#xff09;传递对…

信捷 XDH Ethercat A_MOVER指令

本指令以相对位置运动 如果当前位置在p点&#xff0c;执行本A_MOVER指令结束后&#xff0c;相对p点前移或后退一段距离&#xff0c;这个距离需要在指令里指定。每次执行A_MOVER都是执行前的当前位置为参考点&#xff0c;只要目标位置不为0&#xff0c;都会运动。上图中&#xf…

MyBatis-1:基础概念+环境配置

什么是MyBatis&#xff1f;MyBatis是一款优秀的持久层框架&#xff0c;支持自定义sql&#xff0c;存储过程以及高级映射。MyBatis就是可以让我们更加简单的实现程序和数据库之间进行交互的一个工具。可以让我们更加简单的操作和读取数据库的内容。MyBatis的官网&#xff1a;htt…

弱监督实例分割 Box-supervised Instance Segmentation with Level Set Evolution 论文笔记

弱监督实例分割 Box-supervised Instance Segmentation with Level Set Evolution 论文笔记一、Abstract二、引言三、相关工作3.1 基于 Box 的实例分割3.2 基于层级的分割四、提出的方法4.1 图像分割中的层级模型4.2 基于 Box 的实例分割在 Bounding Box 内的层级进化输入的数据…

elasticsearch 分布式搜索引擎3

1.数据聚合 **聚合&#xff08;aggregations&#xff09;**可以让我们极其方便的实现对数据的统计、分析、运算。例如&#xff1a; 什么品牌的手机最受欢迎&#xff1f;这些手机的平均价格、最高价格、最低价格&#xff1f;这些手机每月的销售情况如何&#xff1f; 实现这些…

Altium Designer(AD)软件使用记录04-AD设计文件输出汇总

目录Altium Designer(AD)软件使用记录04-AD设计文件输出汇总准备工作1、放置层标识&#xff08;标清每个层的顺序&#xff09;2、放置钻孔图&#xff08;表明孔的一些参数&#xff09;3、设置原点坐标一、文件输出1、Gerber文件&#xff08;光绘文件&#xff09;2、钻孔文件3、…

EasyExcell导出excel添加水印

EasyExcell导出excel添加水印1、添加easyExcel相关依赖2、准备基础工具类3、创建水印handler类4、创建单元测试类WriteTest.class5、测试结果1、添加easyExcel相关依赖 <dependency><groupId>org.apache.poi</groupId><artifactId>poi</artifactId&…

Reactor模式

Reactor是一种设计模式&#xff0c;可以用于构建高并发的网络服务器。 Reactor模式的好处在于&#xff1a;可以在一个或多个reactor线程使用多路复用技术去管理所有网络连接连接建立、IO请求&#xff0c;保证工作线程不被IO阻塞。 前置知识&#xff1a;IO多路复用技术 1. 传统网…

如何通过外贸网站SEO优化,提升您的品牌曝光度和海外业务销售额?

随着全球化的不断推进和互联网的普及&#xff0c;越来越多的外贸企业开始重视SEO优化&#xff0c;以提升自身品牌的曝光度和海外业务的销售额。 但是&#xff0c;对于许多外贸企业而言&#xff0c;SEO优化似乎是一个非常陌生的领域&#xff0c;他们并不清楚该如何进行优化。 …

两会特稿 | 项国就书法学术论文入编《中国民族博览》国家一级大型文化类期刊

2022 年 7 月&#xff0c;著名书法家项国就撰写的书法学术论文《探究〈散氏盘〉与其临创感悟》入编国家一级大型文化类期刊《中国民族博览》&#xff0c;并于 2023 年 1 月正式出刊发表。 据悉&#xff0c;《散氏盘》是我国最重要的书法艺术形式之一&#xff0c;研究《散氏盘…

并发编程之AtomicUnsafe

目录 原子操作 定义 术语 处理器如何实现原子操作 处理器自动保证基本内存操作的原子性 使用总线锁保证原子性 使用缓存锁保证原子性 Java当中如何实现原子操作 Atomic 定义 原子更新基本类型类 原子更新数组类 原子更新引用类型 原子更新字段类 Unsafe应用解析…

【MySQL】MySQL的锁机制

目录 概述 MyISAM 表锁 InnoDB行锁 概述 锁是计算机协调多个进程或线程并发访问某一资源的机制&#xff08;避免争抢&#xff09;。 在数据库中&#xff0c;除传统的 计算资源&#xff08;如 CPU、RAM、I/O 等&#xff09;的争用以外&#xff0c;数据也是一种供许多用户共…

中断以及 PIC可编程中断控制器

1 中断分为同步中断&#xff08;中断&#xff09;和异步中断&#xff08;异常&#xff09; 1.1 中断和异常的不同 中断由IO设备和定时器产生&#xff0c;用户的一次按键会引起中断。异步。 异常一般由程序错误产生或者由内核必须处理的异常条件产生。同步。缺页异常&#xff…

【Linux】关机、重启和用户登录注销

目录1 关机&重启命令1.1 基本介绍1.2 注意细节2 用户登录和注销2.1 基本介绍2.2 使用细节1 关机&重启命令 1.1 基本介绍 shutdown -h now          立刻关机shutdown -h 1            “hello&#xff0c;1分钟后会关机了”shutdown -r now   …

【hadoop】介绍

目录 介绍 版本 优势 大数据技术生态体系 介绍 Hadoop是一个由Apache基金会所开发的分布式系统基础架构。 解决 存储和分析计算Google在大数据方面的三篇论文GFS --->HDFS Map-Reduce --->MR BigTable --->HBaseHadoop创始人Doug Cutting版本 Hadoop 三大发行版本&a…

一文带你了解MySQL的Explain

日常工作中&#xff0c;我们经常会收到慢sql告警&#xff0c;前面也写过两篇优化文章&#xff0c;SQL的优化思路和使用规范_出世&入世的博客-CSDN博客 MySQL优化策略_出世&入世的博客-CSDN博客 在优化这些慢sql时&#xff0c;我们经常需要用到explain这个命令来查看…

aws dynamodb java低等级api和高级客户端api的使用

参考资料 https://docs.amazonaws.cn/zh_cn/sdk-for-java/latest/developer-guide/setup-project-maven.html 初始化环境 创建maven项目 mvn org.apache.maven.plugins:maven-archetype-plugin:3.1.2:generate \-DarchetypeArtifactId"maven-archetype-quickstart&quo…

正则表达式引擎NFA自动机的回溯解决方案总结

前几天线上一个项目监控信息突然报告异常&#xff0c;上到机器上后查看相关资源的使用情况&#xff0c;发现 CPU 利用率将近 100%。通过 Java 自带的线程 Dump 工具&#xff0c;我们导出了出问题的堆栈信息。 我们可以看到所有的堆栈都指向了一个名为 validateUrl 的方法&#…

【C语言】每日刷题 —— 牛客语法篇(4)

&#x1f680;&#x1f680;前言 大家好&#xff0c;继续更新专栏 c_牛客&#xff0c;不出意外的话每天更新十道题&#xff0c;难度也是从易到难&#xff0c;自己复习的同时也希望能帮助到大家&#xff0c;题目答案会根据我所学到的知识提供最优解。 &#x1f3e1;个人主页&am…