MySTL实现之MyList

本文介绍了如何实现STL中的List容器,包括结点基类、迭代器和MyList类的详细设计。通过实例展示了插入、删除、遍历等操作,并提供了测试代码以验证功能的正确性。着重讨论了类的继承结构和指针操作,以及在实现过程中遇到的问题和解决方案。
摘要由CSDN通过智能技术生成

MySTL实现

实现STL库中的基础容器及基本函数



MyList

平时很少使用STL中的List容器,因此在实现的时候走了很多弯路,也借鉴了不少博主的实现方式,在结合自己的理解进行了实现。这个容器使用了多个类结合的方式,主要有三大部分,结点基类,迭代器,MyList类,最终MyList类提供对外接口,实现容器的构造,析构和一些基本成员函数。

MyList类图

一、结点的基类

​ 考虑到头节点不需要生成数据域,将结点拆分为指针域和数据域两个结构体表示,结构体内的变量默认为public,继承也是默认public。实现_list_base作为结点的基类,由该类实现对结点的操作。

/// <summary>
/// 指针域
/// </summary>
struct _list_node_pointer {
	_list_node_pointer* _m_next;//指向下一个
	_list_node_pointer* _m_pre;//指向上一个
};
/// <summary>
/// 数据域
/// struct中的变量默认public,继承默认public
/// </summary>
template <typename _T>
struct _list_node_data : public _list_node_pointer {//公有继承指针域
	_T _m_data;
};
template <typename _T>
class _list_node_base {
public:
	_list_node_data<_T>* m_head;
	_list_node_base() {
		m_head = new _list_node_data<_T>();
		m_head->_m_next = m_head;
		m_head->_m_pre = m_head;
	}
	_list_node_base(const _T& val) {
		m_head = new _list_node_data<_T>();
		m_head->_m_next = nullptr;
		m_head->_m_pre = nullptr;
		m_head->_m_data = val;
	}
	~_list_node_base() {
		m_head->_m_next = nullptr;
		m_head->_m_pre = nullptr;
		delete m_head;
	}
};

二、迭代器

迭代器是对指针的操作,为了实现对容器内的访问,在迭代器的实现中,只用指针域作为成员变量即可。实现了构造函数和多种符号的重载。存入基类指针,当需要访问数据域时,基类指针转为子类指针进而访问数据域。

/// <summary>
/// 迭代器的实现
/// 仅需包含一个指针域
/// 对指针进行操作
/// 重载一系列运算符号
/// </summary>
/// <typeparam name="_T"></typeparam>
template<typename _T>
class _list_iterator {
public:
	_list_node_pointer* _m_node;
	_list_iterator():_m_node(nullptr) {}
	_list_iterator(_list_node_pointer* _x) :_m_node(_x){}
	_list_iterator& operator++();//前++
	_list_iterator& operator++(int);//后++
	_list_iterator& operator--();//前--
	_list_iterator& operator--(int);//后--
	_T operator*()const;
	_list_node_data<_T>* operator->()const;
	bool operator!=(const _list_iterator& it) const;
	bool operator==(const _list_iterator& it) const;
};
template<typename _T>
_list_iterator<_T>& _list_iterator<_T>::operator++() {
	_m_node = _m_node->_m_next;
	return *this;
}
template<typename _T>
_list_iterator<_T>& _list_iterator<_T>::operator++(int) {
	_list_iterator old = *this;
	++(*this);
	return old;
}
template<typename _T>
_list_iterator<_T>& _list_iterator<_T>::operator--() {
	_m_node = _m_node->_m_pre;
	return *this;
}
template<typename _T>
_list_iterator<_T>& _list_iterator<_T>::operator--(int) {
	_list_iterator old = *this;
	--(*this);
	return old;
}
template<typename _T>
_T _list_iterator<_T>::operator*() const{
	return ((_list_node_data<_T>*)_m_node)->_m_data;
}
template<typename _T>
_list_node_data<_T>* _list_iterator<_T>::operator->() const {
	return ((_list_node_data<_T>*)_m_node);
}
template<typename _T>
bool _list_iterator<_T>::operator!=(const _list_iterator& it) const {
	return _m_node != it._m_node;
}
template<typename _T>
bool _list_iterator<_T>::operator==(const _list_iterator& it) const {
	return _m_node == it._m_node;
}

三、MyList类

STL中的List是双链表,有一个头节点不存任何数据,仅作为标识。begin()是返回头节点的下一个,end()是返回头节点。发生删除时需要考虑当前List是否为空,并且不能删掉头节点。

删除很好理解,跳过pos结点,前后结点相连即可。对于插入则需要小心,先连好一边再连最后一个,不然中间会断开找不到前驱结点或后继结点,按图中步骤即可在pos前插入val结点。

双链表插入

在MyList类保护继承结点基类( _list_node_base),类中实现了构造函数、析构函数和基本的成员函数,为了方便得到容器的size,将容器长度设为变量,初始化为0。头节点引用基类成员m_head,生成新结点时调用基类构造函数,并由数据域类型( _list_node_data)的指针指向。

template<typename _T>
class MyList :protected _list_node_base<_T> {
public:
	typedef _list_node_data<_T> list_node;
	typedef _list_iterator<_T> iterator;
	typedef _list_node_base<_T> _Base;
protected:
	using _Base::m_head;//结点的指针表示一个双链表
	size_t len;//结点数量
public:
	MyList() :_list_node_base<_T>(),len(0) {};//调用父类默认构造函数
	//有参构造函数
	MyList(size_t _n, const _T& val = _T()) :_list_node_base<_T>(), len(0) {
		for (size_t i = 0; i < _n; i++) {
			insert(begin(), val);
		}
	}
	~MyList() {
		clear();
	}
	/// <summary>
	/// 清空容器
	/// </summary>
	void clear() {
		while (!empty()) {
			pop_back();
		}
	}
	/// <summary>
	/// 返回头结点的下一个
	/// </summary>
	/// <returns></returns>
	iterator begin() {
		return (list_node*)m_head->_m_next;//最后结果为list_node*类型,将其包装为迭代器类型,通过父类指针调用子类构造函数
	}
	/// <summary>
	/// 返回头节点
	/// </summary>
	/// <returns></returns>
	iterator end() {
		return (list_node*)m_head;
	}
	/// <summary>
	/// 获取list长度
	/// </summary>
	/// <returns></returns>
	size_t size() const {
		return len;
	}
	/// <summary>
	/// 判断list是否为空
	/// </summary>
	/// <returns></returns>
	bool empty() const {
		return len == 0;
	}
	/// <summary>
	/// 在pos位插入值为val的结点
	/// </summary>
	/// <param name="pos"></param>
	/// <param name="val"></param>
	/// <returns></returns>
	iterator insert(iterator pos, const _T& val) {
		_Base* b = new _Base(val);
		list_node* _tmp = b->m_head;
		_tmp->_m_next = pos._m_node;
		pos._m_node->_m_pre->_m_next = _tmp;
		_tmp->_m_pre = pos._m_node->_m_pre;
		pos._m_node->_m_pre = _tmp;
		++len;
		return _tmp;
	}
	/// <summary>
	/// 删除pos位的结点
	/// </summary>
	/// <param name="pos"></param>
	/// <returns></returns>
	iterator erase(iterator pos) {
		if (!empty()) {
			list_node* node_next = (list_node*)pos._m_node->_m_next;
			pos._m_node->_m_pre->_m_next = pos._m_node->_m_next;
			pos._m_node->_m_next->_m_pre = pos._m_node->_m_pre;
			//清除结点
			--len;
			return (iterator)node_next;
		}
		else {
			throw "超出list长度!";
		}
	}
	/// <summary>
	/// 在begin位置插入结点
	/// </summary>
	/// <param name="val"></param>
	void push_front(const _T& val = _T()) {
		insert(begin(), val);
	}
	/// <summary>
	/// 在end位置插入结点
	/// </summary>
	/// <param name="val"></param>
	void push_back(const _T& val = _T()) {
		insert(end(), val);
	}
	/// <summary>
	/// 删除begin位置结点
	/// </summary>
	void pop_front() {
		erase(begin());
	}
	/// <summary>
	/// 删除end位置的上一个结点
	/// </summary>
	void pop_back() {
		erase(--end());
	}
};

四、测试

对于MyList中实现的函数进行了简单测试

#include"MyList.h"
#include<iostream>
int main() {
	MyList<int> list1;
	list1.insert(list1.begin(), 1);
	list1.insert(list1.begin(), 2);
	list1.push_back(3);
	list1.push_back(4);
	list1.push_front(5);
	list1.push_front(6);
	std::cout << "List1:" << std::endl;
	std::cout << "List1.size:" << list1.size() << std::endl;
	for (auto it = list1.begin(); it != list1.end(); ++it) {
		std::cout << *it << std::endl;
	}
	list1.erase(list1.begin());
	list1.pop_back();
	list1.pop_front();
	std::cout << "删除后List1.size:" << list1.size() << std::endl;
	std::cout << "删除后List1:" << std::endl;
	for (auto it = list1.begin(); it != list1.end(); ++it) {
		std::cout << *it << std::endl;
	}
	MyList<int> list2(10, 2);
	std::cout << "List2.size:" << list2.size() << std::endl;
	std::cout << "List2:" << std::endl;
	for (auto it = list2.begin(); it != list2.end(); ++it) {
		std::cout << *it << std::endl;
	}
	if (list2.empty()) {
		std::cout << "List2为空" << std::endl;
	}
	else {
		std::cout << "List2不为空" << std::endl;
	}
	return 0;
	
}

总结

简单实现了List容器,对于这个容器使用不多,理解不深,很多概念还是在实现中学到的,其中一些类的设计方法理解还不够深入,发现自己对于指针的使用还有一些问题。在这次实现中,类的成员变量都声明为public了,为了在MyList中可以使用基类对象指针调用其变量,进而可以让数据域指针指向。

_Base* b = new _Base(val);
list_node* _tmp = b->m_head;

该句通过基类构造函数统一构造结点,但是牺牲了安全性,基类成员变量都改为public了。
接下来会实现deque容器及相关函数,MyList类的完整文件在该仓库(如果对你有帮助欢迎star和fork或者你发现了问题都可以在仓库提交问题)也可以从这里下载

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

jiaoooooo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值