【C++】“list”的介绍和常用接口的模拟实现

六点半888 2024-10-07 10:35:01 阅读 73

【C++】“list”的介绍和常用接口的模拟实现

一. list的介绍1. list常见的重要接口2. list的迭代器失效

二. list常用接口的模拟实现(含注释)三. list与vector的对比

一. list的介绍

list是可以在常数范围内任意位置进行插入和删除的序列式容器,并且该容器可以前后双向迭代。list的底层是双向带头链表结构,双向链表中每个元素存储在互不相关的独立节点中,在节点中通过指针指向其前一个元素和后一个元素。list与forward_list非常相似:最主要的不同在于forward_list是单链表只能朝前迭代,已让其更简单高效。与其他的序列式容器相比(array,vector,deque),list通常在任意位置进行插入、移除元素的执行效率更好。与其他序列式容器相比,list和forward_list最大的缺陷是不支持任意位置的随机访问,比如:要访问list的第6个元素,必须从已知的位置(比如头部或者尾部)迭代到该位置,在这段位置上迭代需要线性的时间开销;list还需要一些额外的空间,以保存每个节点的相关联信息。

在这里插入图片描述

1. list常见的重要接口

在这里插入图片描述

在这里插入图片描述

begin与end为正向迭代器,对迭代器执行++操作,迭代器向后移动rbegin(end)与rend(begin)为反向迭代器,对迭代器执行++操作,迭代器向前移动

在这里插入图片描述

2. list的迭代器失效

迭代器失效即迭代器所指向的节点的无效,即该节点被删除了。因为list的底层结构为带头结点的双向循环链表,因此在list中进行插入时是不会导致list的迭代器失效的,只有在删除时才会失效,并且失效的只是指向被删除节点的迭代器,其他迭代器不会受到影响

二. list常用接口的模拟实现(含注释)

<code>#include<assert.h>

#include<iostream>

using std::cout;

using std::endl;

namespace wch

{ -- -->

//由于list中的val支持多种类型,定义模板参数T

template<class T>

struct list_node

{

list_node<T>* _next;

list_node<T>* _prev;

T _val;

//T(),针对自定义类型会去调用它的构造函数,针对内置类型无影响

list_node(const T& val = T())

:_next(nullptr)

, _prev(nullptr)

, _val(val)

{ }

};

// typedef __list_iterator<T, T&, T*> iterator;

// typedef __list_iterator<T, const T&, const T*> const_iterator;

//此处定义多个模板参数针对返回值类型

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* node)

:_node(node)

{ }

Ref operator*() const//重载普通对象解引用

{

return _node->_val;

}

Ptr operator->() const//重载指针对象解引用

{

return &_node->_val;

}

self& operator++()//返回对象在函数体执行结束后依旧存在,建议引用返回,减少拷贝开销

{

_node = _node->_next;

return *this;

}

//返回对象(局部变量或指向局部变量的指针)在函数体执行结束后不存在,不可引用返回,值返回

self operator++(int)

{

self tmp(*this);

_node = _node->_next;

return tmp;

}

self& operator--()

{

_node = _node->_prev;

return *this;

}

self operator--(int)

{

self tmp(*this);

_node = _node->_prev;

return tmp;

}

//尽量使用引用形参, 避免拷贝开销。同时在不需要修改实参时,通过指定const 引用形参来限制。

bool operator!=(const self& it) const

{

return _node != it._node;

}

bool operator==(const self& it) const

{

return _node == it._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迭代器是不行的,因为const迭代器期望指向内容不能修改

// 这样设计是迭代器本身不能修改

// T* const ptr2;

iterator begin()

{

//return _head->_next;//单参构造函数支持隐式类型转换

return iterator(_head->_next);

}

iterator end()

{

return _head;//单参构造函数支持隐式类型转换

//return iterator(_head);

}

const_iterator begin() const

{

//return _head->_next;

return const_iterator(_head->_next);

}

const_iterator end() const

{

return _head;

//return const_iterator(_head);

}

void empty_init()

{

_head = new Node;

_head->_prev = _head;

_head->_next = _head;

_size = 0;

}

list()//无参默认构造函数

{

empty_init();

}

// lt2(lt1)

list(const list<T>& lt)//拷贝构造

//list(const list& lt)//不加类型也可以,不建议

{

empty_init();

for (auto& e : lt)//深拷贝

{

push_back(e);

}

}

void swap(list<T>& lt)

{

std::swap(_head, lt._head);

std::swap(_size, lt._size);

}

list<T>& operator=(list<T> lt)//赋值运算符重载

//list& operator=(list lt)//不加类型也可以,不建议

{

swap(lt);

return *this;

}

~list()//析构函数

{

clear();

delete _head;

_head = nullptr;

}

void clear()

{

iterator it = begin();

while (it != end())

{

it = erase(it);//带位置返回值的erase函数,防止迭代器失效

}

_size = 0;

}

void push_back(const T& x)

{

insert(end(), x);

}

void push_front(const T& x)

{

insert(begin(), x);

}

void pop_back()

{

erase(--end());

}

void pop_front()

{

erase(begin());

}

// pos位置之前插入

iterator insert(iterator pos, const T& x)

{

Node* cur = pos._node;

Node* prev = cur->_prev;

Node* newnode = new Node(x);

prev->_next = newnode;

newnode->_next = cur;

cur->_prev = newnode;

newnode->_prev = prev;

++_size;

return newnode;

}

//删除pos处节点

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;

--_size;

return next;

}

size_t size()

{

/*size_t sz = 0;

iterator it = begin();

while (it != end())

{

++sz;

++it;

}

return sz;*/

return _size;

}

private:

Node* _head;

size_t _size;

};

void Print(const list<int>& lt)

{

list<int>::const_iterator it = lt.begin();

while (it != lt.end())

{

//(*it) += 1;

cout << *it << " ";

++it;

}

cout << endl;

}

void test_list1()

{

list<int> lt;

lt.push_back(1);

lt.push_back(2);

lt.push_back(3);

lt.push_back(4);

list<int>::iterator it = lt.begin();

//由于链表的各个节点地址是不连续的,前一个节点的地址可能比后一个地址大,所以不可已写为 it < it.end();

while (it != lt.end())

{

(*it) += 1;

cout << *it << " ";

++it;

}

cout << endl;

for (auto e : lt)

{

cout << e << " ";

}

cout << endl;

Print(lt);

}

struct A

{

A(int a1 = 0, int a2 = 0)

:_a1(a1)

, _a2(a2)

{ }

int _a1;

int _a2;

};

void test_list2()

{

list<A> lt;

lt.push_back(A(1, 1));

lt.push_back(A(2, 2));

lt.push_back(A(3, 3));

lt.push_back(A(4, 4));

list<A>::iterator it = lt.begin();

while (it != lt.end())

{

//cout << (*it)._a1 << " " << (*it)._a2 << endl;

//it->->_a1, it->->_a2,特殊处理为一个->

cout << it->_a1 << " " << it->_a2 << endl;

++it;

}

cout << endl;

}

void test_list3()

{

list<int> lt;

lt.push_back(1);

lt.push_back(2);

lt.push_back(3);

lt.push_back(4);

lt.push_front(5);

lt.push_front(6);

lt.push_front(7);

lt.push_front(8);

for (auto e : lt)

{

cout << e << " ";

}

cout << endl;

lt.pop_front();

lt.pop_back();

for (auto e : lt)

{

cout << e << " ";

}

cout << endl;

lt.clear();

lt.push_back(10);

lt.push_back(20);

lt.push_back(30);

lt.push_back(40);

for (auto e : lt)

{

cout << e << " ";

}

cout << endl;

cout << lt.size() << endl;

}

void test_list4()

{

list<int> lt;

lt.push_back(1);

lt.push_back(2);

lt.push_back(3);

lt.push_back(4);

for (auto e : lt)

{

cout << e << " ";

}

cout << endl;

list<int> lt1(lt);

for (auto e : lt1)

{

cout << e << " ";

}

cout << endl;

list<int> lt2;

lt2.push_back(5);

lt2.push_back(6);

lt2.push_back(7);

lt2.push_back(8);

for (auto e : lt2)

{

cout << e << " ";

}

cout << endl;

lt1 = lt2;

for (auto e : lt1)

{

cout << e << " ";

}

cout << endl;

}

}

三. list与vector的对比

vector与list都是STL中非常重要的序列式容器,由于两个容器的底层结构不同,导致其特性以及应用场景不同,其主要不同如下:

在这里插入图片描述



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。