C++ —— 模拟实现list
admin
2024-03-15 05:28:17
0

目录

1.链表节点的构建

2.迭代器的初步实现

3.成员变量以及默认构造

4.普通迭代器接口

5.插入接口

6.删除与find接口

7.const迭代器实现与接口

8.范围拷贝与拷贝构造

9.如果实例化参数是自定义类型

10.析构函数

11.完整代码

1.链表节点的构建

链表的节点有指针与和数据域,所以无法用任何一个内置类型来表示它,我们需要自定义好节点的类型。list容器使用的是带头双向循环链表。

	template struct list_node		//节点类型{list_node* _next;list_node* _prev;T _val;list_node(const T& val = T()) :_val(val),_next(nullptr),_prev(nullptr){}};

2.迭代器的初步实现

链表的节点所占用的内存空间是不连续的,所以不能使用原生指针来代替迭代器。我们需要自定义迭代器的行为(例如++是从前一个节点移动到后一个节点)。

template struct list_iterator{typedef list_node node;node* pnode;list_iterator(node* p):pnode(p){}list_iterator& operator++(){pnode = pnode->_next;return *this;}bool operator!=(list_iterator& lt){return pnode != lt.pnode;}};

3.成员变量以及默认构造

定义空容器时,容器是存在头节点(哨兵卫)的。

	template class list{public:typedef list_node node;typedef list_iterator iterator;void empty_init(){_head = new node(T());		//哨兵卫_head->_next = _head;_head->_prev = _head;_size = 0;}list(){empty_init();		//复用}private:node* _head;size_t size;		//记录有节点个数(除哨兵卫)};

4.普通迭代器接口

iterator begin()
{return iterator(_head->_next);
}
iterator end()
{return iterator(_head);		//尾后迭代器
}

5.插入接口

插入有头插、尾插、随机插。我们重点实现随机插,头插和尾插复用随机插。

void push_back(const T& val)
{insert(end(), val);		//在哨兵卫头插就是尾插
}void push_front(const T& val)
{insert(begin(), val);
}iterator insert(iterator pos, const T& val)
{node* newnode = new node(val);node* prev = pos.pnode->_prev;prev->_next = newnode;newnode->_prev = prev;newnode->_next = pos.pnode;pos.pnode->_prev = newnode;++_size;return iterator(newnode);		//返回插入节点的位置(防止迭代器失效)
}

6.删除与find接口

删除有头删、尾删、随机删。我们重点实现随机删,头删和尾删复用随机删。

void pop_back()
{erase(end().pnode->_prev);
}void pop_front()
{erase(begin());
}iterator erase(iterator pos)
{assert(end() != pos);		//不能删除哨兵卫node* prev = pos.pnode->_prev;node* next = pos.pnode->_next;prev->_next = next;next->_prev = prev;delete pos.pnode;--_size;return iterator(next);		//返回删除节点的下一个节点位置(防止迭代器失效)}iterator find(iterator first, iterator last, const T& val)
{assert(first != last);while (first != last){if (*first == val) return first;++first;}return end();
}

7.const迭代器实现与接口

不能使用const成员函数重载,因为我们要的效果是底层const而非顶层const(即指向的内容不可变,迭代器本身可变)。所以我们有两套方案,一是再构建一个迭代器类模板;二是在原来的迭代器模板基础上添加一个模板参数。再构建一个迭代器的方案与原模板的唯一区别就是返回值不同。所以否决第一套设计方案。

现在先统一一下迭代器接口:

typedef list_iterator iterator;
typedef list_iterator const_iterator;const_iterator begin() const
{return const_iterator(_head->_next);
}
const_iterator end() const
{return const_iterator(_head);
}

迭代器设计:

template 		//多使用一个模板参数
struct list_iterator
{typedef list_node node;typedef list_iterator self;		//为了方便node* pnode;list_iterator(node* p):pnode(p){}ref operator*(){return pnode->_val;}self& operator++(){pnode = pnode->_next;return *this;}bool operator!=(self& lt){return pnode != lt.pnode;}
};

8.范围拷贝与拷贝构造

我们实现更加全面的构造接口。

template 
list(InputIterator first, InputIterator last)		//范围拷贝
{empty_init();while (first != last){push_back(*first);++first;}
}void swap(list& lt)
{std::swap(_head, lt._head);std::swap(_size, lt._size);
}
list(const list& lt)		//拷贝构造现代写法
{empty_init();list tmp(lt.begin(), lt.end());swap(tmp);
}list& operator=(list lt)		//赋值运算符重载现代写法
{swap(lt);return *this;
}

9.如果实例化参数是自定义类型

如果链表的节点是一个自定义类型,那么使用 * 将无法读取自定义类型的数据。所以我们需要完善访问自定义类型成员的功能,即 -> 运算符重载。此重载函数的返回值是实例化参数类型的指针,这个指针也有const与非const之分,并且调用此重载的对象可能是const或非const对象,也就是说迭代器可能是const迭代器与非const迭代器。那么我们依然为迭代器模板添加一个参数,并且完善迭代器的功能。

别忘了对迭代器的重命名需要更新一下:

typedef list_iterator iterator;
typedef list_iterator const_iterator;
template 		//多使用一个模板参数
struct list_iterator
{typedef list_node node;typedef list_iterator self;		node* pnode;list_iterator(node* p):pnode(p){}ref operator*(){return pnode->_val;}ptr operator->(){return &pnode->_val;}self& operator++(){pnode = pnode->_next;return *this;}self operator++(int)		//后置++{node* tmp(pnode);pnode = pnode->_next;return tmp;}self& operator--(){pnode = pnode->_prev;return *this;}self operator--(int)		//后置--{node* tmp(pnode);pnode = pnode->_prev;return tmp;}bool operator!=(const self& lt){return pnode != lt.pnode;}bool operator==(const self& lt){return pnode == lt.pnode;}
};

10.析构函数

释放分为两个部分,一是不释放哨兵卫,将有效节点释放;而是全部释放。我们实现一个clear接口,让析构复用此接口。

~list()
{clear();delete _head;_head = nullptr;
}void clear()
{auto it = begin();while (it != end()){it = erase(it);}
}

11.完整代码

这里只实现了list容器常用的接口,并没有完全依照标准库1:1模拟实现。代码会有很多细节没有处理好,并不是会报错,而是有些地方显得不够严谨。

#include 
#include 
namespace ly
{template struct list_node		//节点类型{list_node* _next;list_node* _prev;T _val;list_node(const T& val = T()) :_val(val),_next(nullptr),_prev(nullptr){}};template 		//多使用一个模板参数
struct list_iterator
{typedef list_node node;typedef list_iterator self;		node* pnode;list_iterator(node* p):pnode(p){}ref operator*(){return pnode->_val;}ptr operator->(){return &pnode->_val;}self& operator++(){pnode = pnode->_next;return *this;}self operator++(int)		//后置++{node* tmp(pnode);pnode = pnode->_next;return tmp;}self& operator--(){pnode = pnode->_prev;return *this;}self operator--(int)		//后置--{node* tmp(pnode);pnode = pnode->_prev;return tmp;}bool operator!=(const self& lt){return pnode != lt.pnode;}bool operator==(const self& lt){return pnode == lt.pnode;}
};template class list{public:typedef list_node node;typedef list_iterator iterator;typedef list_iterator const_iterator;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);}void empty_init(){_head = new node(T());		//哨兵卫_head->_next = _head;_head->_prev = _head;_size = 0;}list(){empty_init();		//复用}template list(InputIterator first, InputIterator last)		//范围拷贝{empty_init();while (first != last){push_back(*first);++first;}}void swap(list& lt){std::swap(_head, lt._head);std::swap(_size, lt._size);}list(const list& lt)		//拷贝构造现代写法{empty_init();list tmp(lt.begin(), lt.end());swap(tmp);}list& operator=(list lt)		//赋值运算符重载现代写法{swap(lt);return *this;}void pop_back(){erase(end().pnode->_prev);}void pop_front(){erase(begin());}iterator erase(iterator pos){assert(end() != pos);		//不能删除哨兵卫node* prev = pos.pnode->_prev;node* next = pos.pnode->_next;prev->_next = next;next->_prev = prev;delete pos.pnode;--_size;return iterator(next);		//返回删除节点的下一个节点位置(防止迭代器失效)}iterator find(iterator first, iterator last, const T& val){assert(first != last);while (first != last){if (*first == val) return first;++first;}return end();}void push_back(const T& val){insert(end(), val);		//在哨兵卫头插就是尾插}void push_front(const T& val){insert(begin(), val);}iterator insert(iterator pos, const T& val){node* newnode = new node(val);node* prev = pos.pnode->_prev;prev->_next = newnode;newnode->_prev = prev;newnode->_next = pos.pnode;pos.pnode->_prev = newnode;++_size;return iterator(newnode);		//返回插入节点的位置(防止迭代器失效)}private:node* _head;size_t _size;		//记录有节点个数(除哨兵卫)};
}

相关内容

热门资讯

塞尔达传说荒野之息怎么获得武器... 塞尔达传说荒野之息怎么获得武器?前期武器入手方法一览《塞尔达传说:荒野之息》很多玩家都吐槽赠送武器坏...
口袋妖怪复刻精灵性格能改变吗? 口袋妖怪复刻精灵性格能改变吗?口袋妖怪复刻精灵性格能改变哦,后期可能通过进化来改变哦固定交换能哪来刷...
女人爱上一个人和男人爱上一个人... 女人爱上一个人和男人爱上一个人,有哪些不一样吗?男人和女人是两个完全不同的有机体,在思维、心理和行为...
《哆啦A梦》中哪个片段让你感动... 《哆啦A梦》中哪个片段让你感动?每一集都有精彩的一部分哆啦A 梦真的是陪伴了大雄很久哆啦A梦要回去的...
求不祥之刃符文选择 求不祥之刃符文选择红法穿,蓝减cd,黄成长血,精华法穿,不详前期qe技能陪和法术穿透是很强势的,应为...
超级教师是什么台播放的 超级教师是什么台播放的,乐视TV电视台放超级教师是乐视出品,只能在乐视TV看!
有什么搞笑好看的鬼片? 有什么搞笑好看的鬼片?韩国片主君的太阳!
摘橘子沈从文中夭夭是一个什么样... 摘橘子沈从文中夭夭是一个什么样的女孩摘橘子沈从文中夭夭是一个什么样的女孩?相关内容如下:夭夭是《边城...
作文题目:童心荟萃.主题;节水... 作文题目:童心荟萃.主题;节水。爱水。护水~~╮(╯▽╰)╭ ...
我可不可以和姐夫家的堂兄弟结婚... 我可不可以和姐夫家的堂兄弟结婚?这个是没问题的,不是三代内有血亲关系的都可以
原生家庭对人的影响很大,如何摆... 原生家庭对人的影响很大,如何摆脱原生家庭所造成的性格缺陷?在学校里好好学习,尽量不要被原生家庭的坏习...
你知道哪些珍惜时间的名人故事吗... 你知道哪些珍惜时间的名人故事吗?1、悬梁刺股东汉时候,有个人名叫孙敬,是著名的政治家。他年轻时勤奋好...
有一首歌,歌词里铿锵玫瑰,作何... 有一首歌,歌词里铿锵玫瑰,作何解释?铿 锵 玫 瑰"铿锵"意味着声音宏亮,节奏分明;"玫瑰"代表着美...
“我越爱越深越迷茫,你怎么舍得... “我越爱越深越迷茫,你怎么舍得我心伤,说好一辈子不忘”是什么歌?您好啊!这是一首经典的歌曲---《模...
谁有我们小学时候的课文《八角楼... 谁有我们小学时候的课文《八角楼上的灯光》?《八角楼上的灯光》 回忆起来 上那课的时候宁...
读《从百草园到三味书屋》,谈…... 读《从百草园到三味书屋》,谈……三味书屋的读书生活哪些地方应该改革?至少三方面!教学思想,教学方法,...
腾格里沙漠、巴丹吉林沙漠、库布... 腾格里沙漠、巴丹吉林沙漠、库布齐沙漠哪个更好玩更美?巴丹吉林沙漠腾格里沙漠、巴丹吉林林沙漠、库布齐沙...
AI能回到76人吗 AI能回到76人吗希望AI能回到梦开始的地方 拿下冠军不能 他可能去一只有夺冠实力的球队
她说民谣太穷, 一听就是一根烟... 她说民谣太穷, 一听就是一根烟, 一听就是三瓶酒, 而我只有一根烟了, 还要撑一夜。是什么意思 故...
老井蛙的遗言来自哪则寓言故事 老井蛙的遗言来自哪则寓言故事老井蛙的遗言 在病榻上躺了三个月光景的老井蛙,觉得自己的日子已经不多了,...