STL的list(二):List类的模拟实现 && 迭代器模板(一般)

目录

List类的模拟实现

结点类模板

迭代器类模板

基础模板

*运算符重载

->运算符重载

前后置++、--、==、!=重载

涉及知识点

链表类模板

构造链表对象

 指定位置插入、删除指定位置

begin、end

头尾插、头尾删 

构造、析构、拷贝构造、赋值

求大小、判空

测试函数1

测试函数2

补充内容

打印链表

const迭代器

完整代码

list.h文件

test.cpp文件


List类的模拟实现

结点类模板

template <class T>
struct ListNode
{
	ListNode<T>* _next;//结点的后继指针
	ListNode<T>* _prev;//结点的前驱指针
	T _data;//结点中存放的数据
	
	ListNode(const T& x = T())//构造Node类类型的对象(一个结点对象)
		:_next(nullptr)//未传入指定数据,x就会等于该匿名对象
		,_prev(nullptr)//传入指定数据,x会等于那个指定的数据,T()不起作用
		,_data(x)
	{}
};
  • T() 被调用时,实际上是在调用类型 T 的默认构造函数,生成一个默认初始化的对象。如果 T 是内置类型,T() 会生成一个值为 0 的对象;如果 T 是自定义类型,则会调用该类的默认构造函数

迭代器类模板

基础模板

template<class T>
struct ListIterator
{
	typedef ListNode<T> Node;            //重命名便于在迭代器类使用结点类对象
	typedef ListIterator<T> iterator;    //重命名便于在迭代器类使用迭代器类对象

	Node* _node;
	ListIterator(Node* node)//用_node接收使用迭代器时传递过来的指向链表结点的指针
        :_node(node)        //之所以选择封装是为了对迭代器类和list类进行解耦
	{}                      
};

*运算符重载

//*it
T& operator*()//传引用返回避免了拷贝,且该数据可读可修改
{
	return _node->_data;//解引用迭代器就应该返回一个迭代器指向的对象中存放的数据,在这里是_node指向的结点中存放的数据
}

->运算符重载

T* operator->()//返回值是地址
{
	return &_node->_data;//返回存放当前结点数据_data的地址
}

问题:为什么要返回_data的地址?

答:因为我们这里是要模仿迭代器的功能,而实际中当迭代器指向一个对象时,-> 运算符可以直接通过迭代器访问该对象的成员(普通指针使用 -> 一样),会先解引用迭代器(即 *it,然后访问解引用后的对象的成员变量或成员函数,类似于 (*it).成员,获取的就是该成员的地址

前后置++、--、==、!=重载

//前置++,返回++后的值
iterator& operator++()//传引用返回
{
	_node = _node->_next;//更新迭代器
	return *this;//返回更新结果
}

//后置++,返回++前的值
iterator operator++(int)
{
	iterator tmp(*this);
	_node = _node->_next;//更新迭代器
	return tmp;//返回更新前结果
}

//前置--,返回--后的值
iterator& operator--()
{
	_node = _node->_prev;
	return *this;
}

//后置--,返回--前的值
iterator operator--(int)
{
	iterator tmp(*this);
	_node = _node->_prev;
	return tmp;
}

//不等于,返回判断结果
bool operator!=(const iterator& it)//等价于(const iterator& this,const iterator& it)
{                                 
	return _node != it._node;//等价于this->_node != it.node
}

//等于
bool operator==(const iterator& it)
{
	return _node == it._node;
}

涉及知识点

1、普通迭代器是本身可以修改(++或--等),迭代器指向的内容也可以修改

std::vector<int>::iterator it = vec.begin();
*it = 10;  // 允许修改

2、const迭代器是本身可以修改(++或--等),迭代器指向的内容不可以修改

std::vector<int>::const_iterator cit = vec.begin();
// *cit = 10;  // 错误,不能修改

3、使用typedef的方式只是为了简化在一个类中调用另一个类时可能存在的命名复杂问题,即使没有typedef也可以在一个类中使用另一个类的成员(只要类的成员函数或成员变量的访问权限允许,你可以直接使用类的名称来访问它的成员函数,比如A::Print(),同时记得要在同一命名空间下)

4、后置++和--必须使用传值返回,否则如果传引用返回临时对象tmp,tmp出函数被销毁,使用时就会报错

5、this指针的类型是它所处的类的类型(比如当前实在A类中,调用A类中的非静态成员函数时使用的this的类型就是A*,说是非静态成员函数是因为静态成员函数没有this指针因为它是和类一体的,当然如果非静态成员函数的类型是const那么this的类型就是const A*)

6、*this表示获取之前传递前this指向的一个当前类类型的对象

链表类模板

构造链表对象

template<class T>
class list
{
public:
	typedef ListNode<T> Node;//此时list类可以使用结点类
	typedef ListIterator<T> iterator;//此时list类可以使用迭代器类
	

//初始化一个哨兵位
void empty_init()
{
    _head = new Node();//_head指向一个在堆上构造的Node类类型的匿名结点对象(空结点)
    //不加new的话链表的结点空间在栈上开辟,而不是堆上

    //连接链表首尾
	_head->_next = _head;//_head访问结点对象中的_next指针,并将其中存放的地址是变为_head
	_head->_prev = _head;//_head访问结点对象中的_prev指针,并将其中存放的地址是变为_head
    
    //链表元素个数为0
	_size = 0;
}
	
	list()//无参构造函数
	{
		empty_init();//调用empty_init函数即可
	}

private:
	Node* _head;//_head用于指向链表的哨兵位结点,结点类实例化出的结点对象的类型是Node*,故            
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值