C++——list的实现

news2024/9/25 15:27:27

目录

0.前言

1.节点类 

2.迭代器类 

①普通迭代器

②const迭代器 

③模板迭代器

3.list类 

3.1 clear、析构函数、swap

①clear

② 析构函数 

③ swap

3.2构造函数 

①无参构造

 ②赋值构造

3.3 迭代器

3.4插入函数

①insert插入

②头插

③尾插

3.5 删除函数

①erase删除

②头删 

③尾删

4.测试

源代码(list.h) 


 

0.前言

我们知道,list是一个双向循环链表,所以list的每个节点中需要存在一个指向前一个节点的指针prev、一个指向下一个节点的指针next和一个数据域data

35f348f4278543a9a4d796c80ea00ba8.png

1.节点类 

因为list的底层是节点,而节点的底层又是prev、next指针和数据域data,所以我们先将节点封装为一个类,然后再用list类调用节点类。节点类的代码如下:

//定义链表节点
	template<class T>
	struct ListNode
	{
		ListNode<T>* _next;
		ListNode<T>* _prev;
		T _data;

		//链表节点构造函数
		ListNode(const T& x = T())
			:_next(nullptr)
			, _prev(nullptr)
			, _data(x)
		{}

2.迭代器类 

在string和vector中我们使用迭代器访问数据时需要有这样的操作:

vector<int>::iterator it = l1.begin();
	while (it != l1.end())
	{
		cout << *it << " ";
		++it;
	}
	cout << endl;

 需要知悉的是,在C++中,为了方便和统一,无论什么类我们大多都采用此方法进行访问,string与vector都是连续的,如此访问必然不会有差错,可是list并不是连续的

我们希望++it就能找到下一个节点,而it解引用(*it)就得到节点里存的data,所以,为了使迭代器能够正常访问,我们在自定义的类中必须实现以下方法:

1. 指针可以解引用,迭代器的类中必须重载operator*()

2. 指针可以通过->访问其所指空间成员,迭代器类中必须重载oprator->()

3. 指针可以++向后移动,迭代器类中必须重载operator++()与operator++(int)

4. 迭代器需要进行是否相等的比较,因此还需要重载operator==()与operator!=()

代码如下:

①普通迭代器

//①普通迭代器 可读可写
	template<class T>
	struct __list_iterator
	{
		typedef ListNode<T> Node;
		typedef __list_iterator D;

		Node* _node;

		//迭代器构造函数
		__list_iterator(Node* x)
			:_node(x)
		{}

		//重载++
		//前置++
		D& operator++()//返回迭代器的引用
		{
			_node = _node->_next;//指向下一个节点
			return *this;
		}
		//后置++
		D operator++(int)
		{
			D tmp(*this);

			_node = _node->_next;
			return tmp;//返回拷贝之前的值
		}
		//重载--
		//前置--
		D& operator--()
		{
			_node = _node->_prev;
			return *this;
		}
		//后置--
		D operator--(int)
		{
			D tmp(*this);
			_node = _node->_prev;
			return tmp;
		}

		//重载解引用
		T& operator*()//返回数据的引用
		{
			return _node->_data;//返回节点里的数据
		}
		//重载->
		T* operator->()
		{
			return &_node->_data;
		}

		//重载!=
		bool operator !=(const D& s)
		{
			return _node != s._node;
		}
		//重载==
		bool operator==(const D& s)
		{
			return _node == s._node;
		}
	};

②const迭代器 

const迭代器的作用是只可读不可写,防止数据被修改,因此我们只需在普通迭代器的基础上对operator*()和operator->()添加const操作即可:

//②const迭代器 可读不可写
	template<class T>
	struct __list_const_iterator
	{
		typedef ListNode<T> Node;
		typedef __list_const_iterator D;

		Node* _node;

		//迭代器构造函数
		__list_const_iterator(Node* x)
			:_node(x)
		{}

		//重载++
		//前置++
		D& operator++()//返回迭代器的引用
		{
			_node = _node->_next;//指向下一个节点
			return *this;
		}
		//后置++
		D operator++(int)
		{
			D tmp(*this);

			_node = _node->_next;
			return tmp;//返回拷贝之前的值
		}
		//重载--
		//前置--
		D& operator--()
		{
			_node = _node->_prev;
			return *this;
		}
		//后置--
		D operator--(int)
		{
			D tmp(*this);
			_node = _node->_prev;
			return tmp;
		}

		//重载解引用
		const T& operator*()//返回数据的引用
		{
			return _node->_data;//返回节点里的数据
		}
		//重载->
		const T* operator->()
		{
			return &_node->_data; 
		}

		//重载!=
		bool operator !=(const D& s)
		{
			return _node != s._node;
		}
		//重载==
		bool operator==(const D& s)
		{
			return _node == s._node;
		}
	};

③模板迭代器

观察以上两个迭代器,不同之处也就在于对operator*()和operator->()的操作不同,代码相似度可以说达到了90%,那么有没有办法减少冗余,提高代码的可读性呢?

答案当然是有的,我们可以为两个迭代器提供一个共同的模板,再提供两个参数,当调用普通迭代器和const迭代器时,只需根据所传递的参数而选择不同的迭代器。

template<class T, class Ref, class Ptr>
	struct __list_iterator
	{
		typedef ListNode<T> Node;
		typedef __list_iterator<T, Ref, Ptr> D;

		Node* _node;

		//迭代器构造函数
		__list_iterator(Node* x)
			:_node(x)
		{}

		//重载++
		//前置++
		D& operator++()//返回迭代器的引用
		{
			_node = _node->_next;//指向下一个节点
			return *this;
		}
		//后置++
		D operator++(int)
		{
			D tmp(*this);

			_node = _node->_next;
			return tmp;//返回拷贝之前的值
		}
		//重载--
		//前置--
		D& operator--()
		{
			_node = _node->_prev;
			return *this;
		}
		//后置--
		D operator--(int)
		{
			D tmp(*this);
			_node = _node->_prev;
			return tmp;
		}

		//重载解引用
		Ref operator*()//返回数据的引用
		{
			return _node->_data;//返回节点里的数据
		}
		//重载->
		Ptr operator->()
		{
			return &_node->_data;
		}

		//重载!=
		bool operator !=(const D& s)
		{
			return _node != s._node;
		}
		//重载==
		bool operator==(const D& s)
		{
			return _node == s._node;
		}
	};

3.list类 

做好了节点类和迭代器类的准备工作,终于来到了主角list类

//定义链表
	template<class T>
	class list
	{
		typedef ListNode<T> Node;

	public:
		/*typedef __list_iterator<T> iterator;
		typedef __list_const_iterator<T> const_iterator;*/
		typedef __list_iterator<T, T&, T*> iterator;
		typedef __list_iterator<T, const T&, const T*> const_iterator;

	private:
		Node* _head;
	};

3.1 clear、析构函数、swap

①clear

//clear
	void clear()
	{
		iterator it = begin();
		while (it != end())
		{
			it = erase(it);
		}
	}

② 析构函数 

//析构函数
	~list()
	{
		clear();

		delete _head;
		_head = nullptr;
	}

③ swap

//swap
	void swap(list<T>& tmp)
	{
		std::swap(_head, tmp._head);
	}

3.2构造函数 

①无参构造

//链表构造函数
	list()
	{
		_head = new Node;
		_head->_next = _head;
		_head->_prev = _head;
	}

 ②赋值构造

//operator=
	list<T>& operator=(list<T> lt)
	{
		swap(lt);
		return *this;
	}

3.3 迭代器

//普通迭代器
	iterator begin()
	{
		//return iterator(_head->_next);
		return _head->_next;//单参数的构造函数支持隐式类型转换
	}
	iterator end()
	{
		return _head;
	}

//const迭代器
	const_iterator begin() const
	{
		//return iterator(_head->_next);
		return _head->_next;//单参数的构造函数支持隐式类型转换
	}
	const_iterator end() const
	{
		return _head;
	}

3.4插入函数

①insert插入

//insert插入
iterator insert(iterator pos, const T& x)
{
	Node* cur = pos._node;//取当前节点
	Node* prev = cur->_prev;//当前节点的前一个节点
	Node* newnode = new Node(x);//创建并初始化新节点

	prev->_next = newnode;//前一个节点的_next指针指向新节点
	newnode->_prev = prev;//新节点的_prev指针指向前一个节点
	newnode->_next = cur;//新节点的_next指针指向当前节点(此时相对于新节点就变成了后一个节点)
	cur->_prev = newnode;//当前节点的_prev指针指向新节点(此时相对于新节点就变成了后一个节点)

	//return iterator(newnode);
	return newnode;
}

②头插

//push_front头插
	void push_front(const T& x)
	{
		insert(begin(), x);
	}

③尾插

原始写法

void push_back(const T& x)
	{
		Node* newnode = new Node(x);//开辟并初始化新节点newnode 
		Node* tail = _head->_prev;//定义上一个节点为tail

		tail->_next = newnode;//上一个节点tail的next指针指向新节点newnode
		newnode->_prev = tail;//新节点newnode的prev指针指向上一个节点tail
		newnode->_next = _head;//新节点newnode的next指针指向头节点_head
		_head->_prev = newnode;//头节点_head的prve指针指向新节点newnode
	}

复用insert

void push_back(const T& x)
	{
		insert(end(), x);
	}

复用尾插,写拷贝构造:

//拷贝构造
	list(list<T>& lt)
	{
		_head = new Node;
		_head->_next = _head;
		_head->_prev = _head;//拷贝之前先创建一个头节点,自己指向自己

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

3.5 删除函数

①erase删除

iterator erase(iterator 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 next;
	}

②头删 

//pop_front头删
	void pop_front()
	{
		erase(begin());
	}

③尾删

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

4.测试

void test_list()
	{
		//无参构造
		list<int> l1;
		for (auto e : l1)
		{
			cout << e << " ";
		}
		cout << endl;

		//插入
		//insert插入
		l1.insert(l1.begin(), 1);
		for (auto e : l1)
		{
			cout << e << " ";
		}
		cout << endl;
		//头插
		l1.push_front(0);
		for (auto e : l1)
		{
			cout << e << " ";
		}
		cout << endl;
		//尾插
		l1.push_back(2);
		l1.push_back(3);
		l1.push_back(4);
		for (auto e : l1)
		{
			cout << e << " ";
		}
		cout << endl;

		//删除
		//erase删除
		l1.erase(l1.begin());
		for (auto e : l1)
		{
			cout << e << " ";
		}
		cout << endl;
		//头删
		l1.pop_front();
		for (auto e : l1)
		{
			cout << e << " ";
		}
		cout << endl;
		//尾删
		l1.pop_back();
		for (auto e : l1)
		{
			cout << e << " ";
		}
		cout << endl;

		//赋值构造
		list<int> l2 = l1;
		for (auto e : l1)
		{
			cout << e << " ";
		}
		cout << endl;
	}

86afb71c747f45f981e321057edb713d.png

源代码(list.h) 

#pragma once

#include <iostream>
#include <assert.h>
using namespace std;
#include <assert.h>

namespace xxk
{
	//定义链表节点
	template<class T>
	struct ListNode
	{
		ListNode<T>* _next;
		ListNode<T>* _prev;
		T _data;

		//链表节点构造函数
		ListNode(const T& x = T())
			:_next(nullptr)
			, _prev(nullptr)
			, _data(x)
		{}
	};

	//定义迭代器

	//在vector使用迭代器时:
	/*vector<int>::iterator it = l1.begin();
	while (it != l1.end())
	{
		cout << *it << " ";
		++it;
	}
	cout << endl;*/
	//在这段代码中我们希望++it就能找到下一个节点,而it解引用(*it)我们需要得到节点里存的data
	//可是链表并不是连续的,因迭代器使用形式与指针完全相同,想要实现以上功能,我们必须要在自定义类中实现以下方法:
	//1. 指针可以解引用,迭代器的类中必须重载operator*()
	//2. 指针可以通过->访问其所指空间成员,迭代器类中必须重载oprator->()
	//3. 指针可以++向后移动,迭代器类中必须重载operator++()与operator++(int)
	//   至于operator--() / operator--(int)释放需要重载,根据具体的结构来抉择,双向链表可以向前移动,
	//   所以需要重载,如果是forward_list就不需要重载--
	//4. 迭代器需要进行是否相等的比较,因此还需要重载operator == ()与operator != ()

	//③为减少冗余,提高代码的可读性,使用模板将两个类写到一起
	template<class T, class Ref, class Ptr>
	struct __list_iterator
	{
		typedef ListNode<T> Node;
		typedef __list_iterator<T, Ref, Ptr> D;

		Node* _node;

		//迭代器构造函数
		__list_iterator(Node* x)
			:_node(x)
		{}

		//重载++
		//前置++
		D& operator++()//返回迭代器的引用
		{
			_node = _node->_next;//指向下一个节点
			return *this;
		}
		//后置++
		D operator++(int)
		{
			D tmp(*this);

			_node = _node->_next;
			return tmp;//返回拷贝之前的值
		}
		//重载--
		//前置--
		D& operator--()
		{
			_node = _node->_prev;
			return *this;
		}
		//后置--
		D operator--(int)
		{
			D tmp(*this);
			_node = _node->_prev;
			return tmp;
		}

		//重载解引用
		Ref operator*()//返回数据的引用
		{
			return _node->_data;//返回节点里的数据
		}
		//重载->
		Ptr operator->()
		{
			return &_node->_data;
		}

		//重载!=
		bool operator !=(const D& s)
		{
			return _node != s._node;
		}
		//重载==
		bool operator==(const D& s)
		{
			return _node == s._node;
		}
	};

	//定义链表
	template<class T>
	class list
	{
		typedef ListNode<T> Node;

	public:
		/*typedef __list_iterator<T> iterator;
		typedef __list_const_iterator<T> const_iterator;*/
		typedef __list_iterator<T, T&, T*> iterator;
		typedef __list_iterator<T, const T&, const T*> const_iterator;

		//普通迭代器
		iterator begin()
		{
			//return iterator(_head->_next);
			return _head->_next;//单参数的构造函数支持隐式类型转换
		}
		iterator end()
		{
			return _head;
		}

		//const迭代器
		const_iterator begin() const
		{
			//return iterator(_head->_next);
			return _head->_next;//单参数的构造函数支持隐式类型转换
		}
		const_iterator end() const
		{
			return _head;
		}

		//链表构造函数
		list()
		{
			_head = new Node;
			_head->_next = _head;
			_head->_prev = _head;
		}

		//clear
		void clear()
		{
			iterator it = begin();
			while (it != end())
			{
				it = erase(it);
			}
		}

		//析构函数
		~list()
		{
			clear();

			delete _head;
			_head = nullptr;
		}

		//拷贝构造
		list(list<T>& lt)
		{
			_head = new Node;
			_head->_next = _head;
			_head->_prev = _head;//拷贝之前先创建一个头节点,自己指向自己

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

		//swap
		void swap(list<T>& tmp)
		{
			std::swap(_head, tmp._head);
		}

		//operator=
		list<T>& operator=(list<T> lt)
		{
			swap(lt);
			return *this;
		}

		//尾插
		①
		//void push_back(const T& x)
		//{
		//	Node* newnode = new Node(x);//开辟并初始化新节点newnode 
		//	Node* tail = _head->_prev;//定义上一个节点为tail

		//	tail->_next = newnode;//上一个节点tail的next指针指向新节点newnode
		//	newnode->_prev = tail;//新节点newnode的prev指针指向上一个节点tail
		//	newnode->_next = _head;//新节点newnode的next指针指向头节点_head
		//	_head->_prev = newnode;//头节点_head的prve指针指向新节点newnode
		//}
		//②复用insert
		void push_back(const T& x)
		{
			insert(end(), x);
		}

		//insert插入
		iterator insert(iterator pos, const T& x)
		{
			Node* cur = pos._node;//取当前节点
			Node* prev = cur->_prev;//当前节点的前一个节点
			Node* newnode = new Node(x);//创建并初始化新节点

			prev->_next = newnode;//前一个节点的_next指针指向新节点
			newnode->_prev = prev;//新节点的_prev指针指向前一个节点
			newnode->_next = cur;//新节点的_next指针指向当前节点(此时相对于新节点就变成了后一个节点)
			cur->_prev = newnode;//当前节点的_prev指针指向新节点(此时相对于新节点就变成了后一个节点)

			//return iterator(newnode);
			return newnode;
		}

		//push_front头插
		void push_front(const T& x)
		{
			insert(begin(), x);
		}

		//erase删除函数
		iterator erase(iterator 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 next;
		}

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

	private:
		Node* _head;
	};

	void test_list()
	{
		//无参构造
		list<int> l1;
		for (auto e : l1)
		{
			cout << e << " ";
		}
		cout << endl;

		//插入
		//insert插入
		l1.insert(l1.begin(), 1);
		for (auto e : l1)
		{
			cout << e << " ";
		}
		cout << endl;
		//头插
		l1.push_front(0);
		for (auto e : l1)
		{
			cout << e << " ";
		}
		cout << endl;
		//尾插
		l1.push_back(2);
		l1.push_back(3);
		l1.push_back(4);
		for (auto e : l1)
		{
			cout << e << " ";
		}
		cout << endl;

		//删除
		//erase删除
		l1.erase(l1.begin());
		for (auto e : l1)
		{
			cout << e << " ";
		}
		cout << endl;
		//头删
		l1.pop_front();
		for (auto e : l1)
		{
			cout << e << " ";
		}
		cout << endl;
		//尾删
		l1.pop_back();
		for (auto e : l1)
		{
			cout << e << " ";
		}
		cout << endl;

		//赋值构造
		list<int> l2 = l1;
		for (auto e : l1)
		{
			cout << e << " ";
		}
		cout << endl;
	}
}

 

 

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

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

相关文章

【分治】归并排序

【分治】归并排序 1. 排序数组1. 1题目来源1.2 题目描述1.3 题目解析 2. LCR 170. 交易逆序对的总数2. 1题目来源2.2 题目描述2.3 题目解析 3. 计算右侧小于当前元素的个数3. 1题目来源3.2 题目描述3.3 题目解析 1. 排序数组 1. 1题目来源 912. 排序数组 1.2 题目描述 给你…

JAVA毕业设计171—基于Java+Springboot+vue3+小程序的宠物店小程序系统(源代码+数据库)

毕设所有选题&#xff1a; https://blog.csdn.net/2303_76227485/article/details/131104075 基于JavaSpringbootvue3小程序的宠物店小程序系统(源代码数据库)171 一、系统介绍 本项目前后端分离(可以改为ssm版本)&#xff0c;分为用户、店员、管理员三种角色 1、用户&…

存储课程学习笔记1_访问scsi磁盘读写测试(struct sg_io_hdr,ioctl,mmap)

创建虚拟机时&#xff0c;可以选择SCSI,STAT,NVME不同类型的磁盘。 0&#xff1a;总结 》了解内核提供的访问scsi的结构和方法 &#xff08;主要是sg_io_hdr_t 结构体和ioctl函数&#xff09;。 》需要读scsi协议文档&#xff0c;了解相关指令&#xff0c;只演示了16字节固定…

华为eNSP :WLAN的配置

一、WLAN的知识点&#xff1a; VLAN配置&#xff1a; VLAN&#xff1a;可以想象成一个大房子&#xff08;网络&#xff09;里划分的不同房间&#xff08;VLAN&#xff09;。每个房间可以有自己的功能&#xff0c;比如一个用于睡觉&#xff08;管理&#xff09;&#xff0c;另一…

软件工程知识点总结(5):详细设计

面向对象详细设计举例&#xff1a;接口描述、算法描述、数据描述 类的详细描述&#xff0c;内含数据、 方法及方法的参数返回值 public class User { private String userId; private String userName; private String password; private int type; public User(String userId…

基于APISIX实现API网关案例分享

一、APISIX介绍 1、定义 Apache APISIX 是一个动态、实时、高性能的云原生 API 网关。它构建于 NGINX + ngx_lua 的技术基础之上,充分利用了 LuaJIT 所提供的强大性能。 2、软件架构 2.1、架构图 APISIX 主要分为两个部分: APISIX 核心:包括 Lua 插件、多语言插件运行时…

ICM20948 DMP代码详解(12)

接前一篇文章&#xff1a;ICM20948 DMP代码详解&#xff08;11&#xff09; 上一回开始解析icm20948_sensor_setup函数的第2段代码也即inv_icm20948_init_matrix函数&#xff1a; /* Setup accel and gyro mounting matrix and associated angle for current board */inv_icm20…

前端技术(七)——less 教程

一、less简介 1. less是什么&#xff1f; less是一种动态样式语言&#xff0c;属于css预处理器的范畴&#xff0c;它扩展了CSS语言&#xff0c;增加了变量、Mixin、函数等特性&#xff0c;使CSS 更易维护和扩展LESS 既可以在 客户端 上运行 &#xff0c;也可以借助Node.js在服…

关于武汉芯景科技有限公司的IIC缓冲器芯片XJ4307开发指南(兼容LTC4307)

一、芯片引脚介绍 1.芯片引脚 2.引脚描述 二、系统结构图 三、功能描述 1.总线超时&#xff0c;自动断开连接 当 SDAOUT 或 SCLOUT 为低电平时&#xff0c;将启动内部定时器。定时器仅在相应输入变为高电平时重置。如果在 30ms &#xff08;典型值&#xff09; 内没有变为高…

社交媒体的未来:Facebook如何通过AI技术引领潮流

在数字化时代的浪潮中&#xff0c;社交媒体平台不断演变&#xff0c;以适应用户需求和技术发展的变化。作为全球领先的社交媒体平台&#xff0c;Facebook在这一进程中扮演了重要角色。尤其是人工智能&#xff08;AI&#xff09;技术的应用&#xff0c;正在深刻地改变Facebook的…

Docker零基础入门

参考课程https://www.bilibili.com/video/BV1VC4y177re/?vd_source=b15169a302bee35f484245aecc69d4dd 参考书籍Docker 实践 - 面向 AI 开发人员的 Docker 实践 (dockerpractice.readthedocs.io) 1. 什么是Docker 1.1. Docker起源 随着计算机的发展,计算机上已经可以运行多…

Stable Diffusion绘画 | ControlNet应用-Inpaint(局部重绘):更完美的重绘

Inpaint(局部重绘) 相当于小号的AI版PS&#xff0c;不但可以进行局部画面的修改&#xff0c;还可以去除背景中多余的内容&#xff0c;或者是四周画面内容的扩充。 预处理器说明 Inpaint_Global_Harmonious&#xff1a;重绘-全局融合算法&#xff0c;会对整个图片的画面和色调…

【无标题】SAM(Segment Anything Model)

1.SAM是什么&#xff1f; SAM是基于NLP的一个基础模型&#xff0c;专注于提示分割任务&#xff0c;使用提升工程来适应不同的下游分割任务。 2.SAM有什么用&#xff1f; 1&#xff09;SAM 可以通过简单地单击或交互选择要包含或排除在对象中的点来分割对象。还可以通过使用多边…

成都爱尔综合眼病科李晓峰主任解析空调续命,干眼别忍!

高温酷暑&#xff0c;命都是空调给的。 凉风一直吹&#xff0c;根本不敢停。 热到大汗淋漓&#xff0c;身体缺水&#xff0c;眼睛也是。 屋外闷热湿度不低&#xff0c;屋内空调一开湿度“骤降”不够用。 房间被“除湿”&#xff0c;眼睛也不例外。 长时间吹空调&#xff0c…

基于C++实现(控制台)模拟网上购书订单管理系统

模拟网上购书订单管理系统&#xff08;大一小学期C大作业&#xff09; 一、任务 1. 基础任务 建立继承了Buyer类的三个子类作为顾客的三种类型&#xff0c;用于管理顾客对象&#xff1b;建立Book类&#xff0c;管理书本对象&#xff1b;根据不同类型的顾客&#xff0c;计算出…

全球主要指数年度收益率汇总

1 美国 1.1 道琼斯工业平均指数 DJIA 1.2 纳斯达克综合指数 IXIC 1.3 纳斯达克100指数 NDX 1.4 标准普尔500 INX 2 中国 2.1 国债指数 000021 2.2 上证综指 000001 2.3 深证成指 399001 2.4 创业板 399006 2.5 中小100 399005 2.6 上证50 000016 3 香港

智能可视耳勺怎么用?智能可视耳勺使用方法!

随着科技的进步&#xff0c;有很多人摒弃了传统挖耳勺&#xff0c;选择更加高效直观的智能可视耳勺&#xff0c;这是因为智能可视耳勺能更加直观地看到耳朵的内部&#xff0c;让掏耳过程清晰明了&#xff0c;精准掏出耳垢。 但市场有的智能可视耳勺鱼龙混杂&#xff0c;很多人在…

【解决】vue 弹窗后面页面可以滚动问题

做web端项目过程中&#xff0c;发现点击弹窗后&#xff0c;弹窗后面的页面还可以滚动。 复现如下&#xff1a; 【方法1】 step1&#xff1a;在弹框页面使用 mousewheel.prevent <divv-show"workShowMenu"mousewheel.prevent>// TO DO...弹框内容 </div&…

C盘清理 拯救你的C盘!C盘从此不再爆满~!

C盘清理&#xff0c;拯救你的C盘&#xff01;C盘从此不再爆满~&#xff01;C盘爆满是许多人经常遇到的问题&#xff0c;它可能导致系统运行缓慢甚至崩溃&#xff0c;对于这种情况&#xff0c;我们要从根源触发&#xff0c;彻底的清理干净C盘垃圾。 一般的C盘清理有下面几种方法…

AI跟踪报道第55期-新加坡内哥谈技术-本周AI新闻: GPT NEXT (x100倍)即将在2024推出

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…