【list】【手撕 STL】List 容器全解析!迭代器 / 增删改查 / 去重排序,面试必背的核心考点!

文章目录

list

不支持通过下标访问(operator ),不支持下标加减,支持++和--(访问下一个和前一个)






迭代器的分类

  1. 按照功能
    1. iterator
    2. reverse_iterator
    3. const iterator
    4. const reverse_iterator
  2. 按照性质:
    1. 单向(forward iterator):forward_list/unordered_map/unordered_set------>只支持++
    2. 双向(bidirectional iterator):list/map/set------>支持++/--
    3. 随机(random access iterator):string/vector/deque------>支持++/--/+/-
  • sort:只支持随机迭代器

    template <class RandomAccessIterator> void sort (RandomAccessIterator first, RandomAccessIterator last);

  • reverse :支持随机和双向

    template <class BidirectionalIterator>

    void reverse (BidirectionalIterator first, BidirectionalIterator last);

  • find:都可(是InputIterator,是单向迭代器的子类)






emplace_back

  • emplace_back和push_back的区别
cpp 复制代码
struct A
{
public:
    A(int a1=1,int a2=1)
        :_a1(a1)
        ,_a2(a2)
    { }

private: 
    int _a1;
    int _a2;
};

void test()
{
    list<A>lt;
    A a(1, 1);

    lt.push_back(a);
    lt.push_back(A(3, 4));//支持匿名对象

    lt.emplace_back(a);
    lt.emplace_back(A(3, 4));//支持匿名对象
    lt.emplace_back( 3, 4 ); //支持直接传构造A的参数

}





实现中间插入

list 是不支持迭代器的+/-找到下标的,但我们也可以先通过其他方式找到下标,再insert




  • 在下标为3的位置前面插入
cpp 复制代码
void test1()
{
    list<int>lt;

    lt.push_back(1); 
    lt.push_back(2);
    lt.push_back(3);
    lt.push_back(4);
    lt.push_back(5);

    auto it = lt.begin();

    int k = 3;

    while (k--)
    {
        it++;
    }

    lt.insert(it, 66);

    for (auto e : lt)
    {
        cout << e << " ";
    }
    cout << endl;


}

int main() 
{ 
    test1();
    return 0;
}





erase

  • 删除4:
    • 要判断:(it!=lt.end()),因为如果find没有找到,会返回最后一个迭代器
cpp 复制代码
    it = find(lt.begin(), lt.end(), 4);
    if(it!=lt.end())
    {
        lt.erase(it);
    }





sort排序:升/降

  • 升序: lt.sort( );

  • 降序:

    greater<int>gt; ​ lt.sort(gt);

    或者直接写成: lt.sort(greater<int>())匿名对象






merge:合并链表

  • A和B都是有序的才能合并
  • A.merge(B)后**,B 变为空链表(而非销毁,仍可复用)。**
cpp 复制代码
list<int>lt;

lt.push_back(1); 
lt.push_back(2);
lt.push_back(3);
lt.push_back(4);
lt.push_back(5);

list<int>lt2;

lt2.push_back(3);
lt2.push_back(4);
lt2.push_back(5); 
lt2.push_back(6);

lt.merge(lt2);

for (auto e : lt)
{
    cout << e << " ";
}
cout << endl;





unique:去重

  • 必须是有序的才能使用(相同的值都挨在一起)------>先sort再去重
cpp 复制代码
    list<int>lt;

    lt.push_back(1); 
    lt.push_back(2);
    lt.push_back(3);
    lt.push_back(4);
    lt.push_back(5);



    list<int>lt2;

    lt2.push_back(3);
    lt2.push_back(4);
    lt2.push_back(5); 
    lt2.push_back(6);

    lt.merge(lt2);

 
    for (auto e : lt)
    {
        cout << e << " ";
    }
    cout << endl;



    lt.unique();

    for (auto e : lt )
    {
        cout << e << " ";
    }
    cout << endl;
cpp 复制代码
1 2 3 3 4 4 5 5 6
1 2 3 4 5 6





remove:删除

传的是值,不是迭代器

cpp 复制代码
    lt.remove(2);
    lt.remove(3);





splice:转移元素

3种用法:(被转移的结构的对应元素会消失)

cpp 复制代码
entire list (1)	//转移整个链表
	void splice (iterator position, list& x);
single element (2)	//转移单个迭代器位置的元素
	void splice (iterator position, list& x, iterator i);
element range (3)	//转移某个范围
	void splice (iterator position, list& x, iterator first, iterator last);



举例:

  • 构建一个1,2,3,4,5,6的链表:

    cpp 复制代码
        list<int>lt;
      
        lt.push_back(1); 
        lt.push_back(2);
        lt.push_back(3);
        lt.push_back(4); 
        lt .push_back(5); 
        lt .push_back(6);



  • 将某个数字移到开头:
cpp 复制代码
    int x = 0;
    cin >> x;

    auto it = find(lt.begin(),lt.end(), x);

    ///void splice (iterator position, list& x, iterator i);
    lt.splice(lt.begin(), lt, it);
    



  • 将某个数字及其之后的所有数字移到开头:
cpp 复制代码
    int x = 0;
    cin >> x;

    auto it = find(lt.begin(),lt.end(), x);

    ///void splice (iterator position, list& x, iterator first, iterator last);
    lt.splice(lt.begin(), lt, it, lt.end());





实现

三个类

✅ **三层结构 **

  • 第1层:list_node 节点类(存数据和前后指针)
  • 第2层:list_iterator 迭代器类(专门封装节点指针,用来重载 ++、--、*、->运算符,让链表能用像指针一样的语法遍历)
  • 第3层:list 容器类(对外提供接口,管理整个链表)

✅ **迭代器单独成类的原因 ** 运算符重载要求至少有一个操作数是类对象,原生指针的++是地址 + 1,不符合链表节点不连续的内存结构,所以必须把节点指针包装成一个类,自己定义++的行为。

list.h

在namespace lcj中,创建class list






  • 先实现链表节点list_node,为一个结构体(类),其中存 内容,上一个节点指针,下一个节点指针
  • 实现list类:list
cpp 复制代码
namespace lcj
{
	template<class T>
	class list_node
	{
		T _data;
		list_node<T>* _prev;
		list_node<T>* _next;

	};


	template<class T>
	class list 
	{
		typedef list_node<T> Node;

	public: 

	private:
		Node* _head;

	};
}





1.构造函数:

  1. 创建节点
  2. 将节点中的前后指针加上

_head = new Node; ​ _head->_next = _head; ​ _head->_prev = _head;

cpp 复制代码
namespace lcj
{
	template<class T>
	class list_node
	{
		T _data;
		list_node<T>* _prev;
		list_node<T>* _next;

	};


	template<class T>
	class list 
	{
		typedef list_node<T> Node;

	public:
		list()//////////////////////////////////////////////////////////////
		{
			_head = new Node;
			_head->_next = _head;
			_head->_prev = _head;

		}

	private:
		Node* _head;

	};
}





2.实现尾插:

实现size()和empty()

为了计数方便,增加_size

cpp 复制代码
	template<class T>
	class list 
	{
		typedef list_node<T> Node;

	public: 

	private:
		Node* _head;
		size_t _size;/////////////////////////////////////////////////////////////

	};



push_back

cpp 复制代码
	template<class T>
	class list 
	{
		typedef list_node<T> Node;

	public:
		list()
		{
			_head = new Node;
			_head->_next = _head;
			_head->_prev = _head;
             _head->_size =0; 
		}

		void push_back(const T& x)
		{
			Node* newnode = new Node(x);


			Node* tail = _head->_prev; // 先拿到原来的尾节点

			newnode->_next = _head;    // 新节点的后继指向头节点
			newnode->_prev = tail;     // 新节点的前驱指向旧尾

			tail->_next = newnode;     // 旧尾的后继指向新节点
			_head->_prev = newnode;    // 头节点的前驱指向新节点  

			++_size;
		}

		size_t size() const///////////////////////////
		{
			return _size;
		}

		bool empty() const//////////////////////////////
		{
			return _size == 0;
		}

	private:
		Node* _head;
		size_t _size;

	};





3.实现迭代器

为什么要自己实现一个迭代器,因为list的节点在内存中不是线性存储的。如果是原来迭代器的一些操作,比如++、--,可能造成访问越界,所以我们要自己实现一个类,从而实现List的迭代器对应的操作

运算符重载函数,是专门为类类型准备的------>参数至少有一个自定义类型:不能参数全是 int、double 这种内置类型, 防止你修改 C++ 原本的语法。






代码+逐句讲解‼️

cpp 复制代码
	template<class T>
	struct list_iterator//迭代器的类
	{
		typedef list_node<T> Node;//简化节点名称
        
        
        
		Node* _node;//这是整个迭代器这个类中   唯一的成员变量
        //为什么要有这个成员变量呢?
        //因为我们知道迭代器的作用就是返回一个节点的指针,
        //所以我们创造这个成员变量,
            //然后呢,在后面的操作中直接对这个指针进行修改,
           // 然后可以直接返回它
        
        
        
        
		typedef list_iterator Self; //简化迭代器这个名字,在迭代器最低的类中,就叫self

        
        
        
		list_iterator(Node* node)//迭代器这个类的 构造函数------>支持list类中的这样  iterator it(_head->_next);的使用。用来在类外把对应的节点指针变成迭代器
			:_node(node)
		{ }


		T& operator*()//*解引用的运算符重载,直接返回list对应的数据_data
		{
			return _node->_data; 
		}

		Self  operator++()//返回下一个节点的迭代器,返回的是迭代器这个类, // 必须返回自身引用,符合STL迭代器规范
		{
			_node = _node->_next;
			return *this;
		}
        
        Self&  operator++()
		{
			_node = _node->_next;
			return *this;
		}

		Self&  operator--()
		{
			_node = _node->_prev;
			return *this;
		}

		bool operator!=(const Self& s)
		{
			return _node != s._node;
		}

		bool operator==(const Self& s)const
		{
			return _node == s._node;
		}
	};

struct 和class:

  1. struct类,不加访问限定符,纯公有
  2. class,不加,纯私有

1.为什么list_node用 struct?

节点类是一个纯数据结构 ,它的三个成员_data_prev_next,需要被list类和list_iterator类随时随地直接读写。

2. 为什么list_iterator用 struct?

它的核心成员_node指针,需要被list类的成员函数(begin()end()insert()erase())直接访问。比如:

​ ``bool operator!=(const Self& s){return _node != s._node; ​ }




把list_node改为struct

cpp 复制代码
	struct  list_node
	{
		T _data;
		list_node<T>* _prev;
		list_node<T>* _next;

	};








test_list1()

cpp 复制代码
	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();

		while (it != lt.end())
		{
			cout << *it << " ";
			++it;
		}
		cout << endl;
	}

push_back上面说了,

接下来,想要实现:list<int>::iterator it = lt.begin();,就要先实现

  1. 迭代器iterator
  2. begin函数
  • Iterator是我们自己定义的一个类:struct list_iterator//迭代器,然后再list这个类中typedef出来的名称

    • 我们是这样创建list的迭代器的,list<int>::iterator it = lt.begin();,但看起来这个iterator它是在list类这个类里面的,可是我们知道并不是这样,

      ------>因为list类内部有一个typedef(类型别名),把外部的list_iterator<T>重命名成了内部的iterator

      ------>编译器会自动把它翻译成: list_iterator<int> it = lt.begin();






构造函数的对比

  • 每一个类都有对应的构造函数

    • 构造一个节点的时候

      cpp 复制代码
      		list_node(const T& data = T())
      			:_data(data)
      			,_next(nullptr)
      			,_prev(nullptr)
      		{ }
    • 构造一个list的时候

      cpp 复制代码
      		list()
      		{
      			_head = new Node(T());
      			_head->_next = _head;
      			_head->_prev = _head;
      			_size = 0; 
      		}
    • 构造一个迭代器的时候

      因为我们在实现begin、 and的时候,一般是这样的:

      iterator it(_head->_next);

      return it;

      所以迭代器的构造函数要是带参的

      cpp 复制代码
      		list_iterator(Node* node)
      			:_node(node)
      		{ }

4.前一部分总代码+逐句讲解‼️

下面的end函数使用了隐式类型转换,相关知识请看这里

指针类型直接转化成了一个iterator这个类类型,相当于内置类型转化成类类型, 条件就是类有对应的构造函数就可以

cpp 复制代码
namespace lcj
{
    
    
    
    /////////////////////////////////////////////////////////////////////////////////////////////
	template<class T>
	struct  list_node//节点的类
	{
		T _data;
		list_node<T>* _prev;
		list_node<T>* _next;

		list_node(const T& data = T())//节点的实现,是一个默认 构造函数, 并且同时支持你传参和不传参都可以生成对应的节点
			:_data(data)
			,_next(nullptr)
			,_prev(nullptr)
		{ } 
	};

    
    
    
    
    /////////////////////////////////////////////////////////////////////////////////////////////
	template<class T>
	struct list_iterator//迭代器的类
	{
		typedef list_node<T> Node;//简化节点名称
		Node* _node;//这是整个迭代器这个类中   唯一的成员变量,是一个节点的指针。因为像是 ++、--、*这些操作,都需要通过这个指针找到对应的节点(Node* 类型),然后返回对应的迭代器
        
        
        
		typedef list_iterator Self; //在自己这个类中简化自己这个类的名称,然后在后面的++、--中,返回时也写得更方便一些

        
        //传参传的是(_head->_next)这样的节点指针。
        //用来在类外把对应的节点指针变成list_iterator迭代器这种类
		list_iterator(Node* node)//迭代器这个类的 构造函数------>支持list类中的这样  iterator it(_head->_next);的使用 
			:_node(node)
		{ }


		T& operator*()//*的运算符重载,直接返回list对应的_data
		{
			return _node->_data; 
		}

		Self&  operator++()//++的运算符重载,返回对应的迭代器
		{
			_node = _node->_next;
			return *this;
		}

		bool operator!=(const Self& s)//通过判断迭代器类(list_iterator)创建的对象  中唯一的成员变量_node ,来判断两个迭代器类(list_iterator)是否是相同的迭代 类 对象(list_iterator)
		{
			return _node != s._node;
		}
	};

    
    
    
    
    
	/////////////////////////////////////////////////////////////////////////////////////////////
	template<class T>
	class list // 链表的类
	{
		typedef list_node<T> Node;//简化链节的写法

	public:
		list()// 构造函数,生成一个只有头节点的list
		{
			_head = new Node(T());
			_head->_next = _head;
			_head->_prev = _head;
			_size = 0; 
		}

        
        
		typedef list_iterator<T> iterator;//简化迭代器的写法,符合STL

		iterator begin()//通过 节点指针,创造出一个迭代器类(iterator),然后返回这个迭代器类
		{
			iterator it(_head->_next);
			return it;//编译器会做值拷贝,把局部迭代器里的_node指针复制给外面接收的变量
		}

		iterator end()//指向哨兵头节点(最后一个节点的下一个节点),作为遍历结束的标志,创造出一个迭代器类(iterator),然后返回这个迭代器类
		{
			return _head;
		}


		void push_back(const T& x)
		{
			Node* newnode = new Node(x);//创建节点,并且调用带参构造函数


			Node* tail = _head->_prev; // 先拿到原来的尾节点

			newnode->_next = _head;    // 新节点的后继指向头节点
			newnode->_prev = tail;     // 新节点的前驱指向旧尾

			tail->_next = newnode;     // 旧尾的后继指向新节点
			_head->_prev = newnode;    // 头节点的前驱指向新节点  

			++_size;
		}

		size_t size()
		{
			return _size;
		}

		bool empty() const
		{
			return _size == 0;
		}

	private:
		Node* _head;
		size_t _size;

	};


	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();

		while (it != lt.end())
		{
			cout << *it << " ";
			++it;
		}
		cout << endl;
	}
}





5.实现insert

通过insert实现push_front和push_back

cpp 复制代码
		void insert(iterator pos, const T& x)
		{
			Node* cur = pos._node;///这里应该写成.而不是->,因为 pos是个类(结构体),_node是结构体的成员变量
			Node* prev = cur->_prev;

			Node* newnode = new Node(x);

			newnode->_next = cur;
			cur->_prev = newnode; 
			newnode->_prev = prev;
			prev->_next = newnode;

			++_size;
		}

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

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





6.实现erase

通过erase实现pop_back

通过erase实现pop_front

cpp 复制代码
		void erase(iterator pos)
		{
			assert(pos != end());///end是哨兵位头节点

			///迭代器只是一个类,想要访问对应的节点需要访问其中的_node成员变量
			Node* prev = pos._node->_prev;
			Node* next= pos._node->_next;

			prev->_next = next;
			next->_prev = prev;

			delete pos._node;
			--_size;
		}

		void pop_back()
		{
			erase(--end());
		}

		void pop_front()
		{
			erase(begin());
		}





7.重载->

想打印一个结构体的类,而且

如下,我定义了一个结构体struct AA,创建了对应的list:list<AA> lta

想打印,

  • 方法一:当然可以先:list<AA>::iterator it = lta.begin();,找到对应元素的指针(AA类的指针),然后解引用,得到一个AA类,然后通过"."来访问对应的_a1和_a2
  • 方法二:list的迭代器,就是一个结构AA对象的地址------>通过"->",像访问一个结构体一样访问不行吗?不行🙅‍♀️,因为这时候list的迭代器并不是地址,而是一个类,所以不能用"->"
cpp 复制代码
	struct AA
	{
		int _a1=1;
		int _a2=2;
	};


		list<AA> lta;
		lta.push_back(AA());
		lta.push_back(AA());
		lta.push_back(AA());
		lta.push_back(AA());
		lta.push_back(AA());


		list<AA>::iterator it = lta.begin();

		while (it != lta.end())
		{
			///使用.的方法
			cout << (*it)._a1 << " : " << (*it)._a2 << endl;

			❌❌❌///使用->的方法:重载
			cout<<  it->_a1 << " : " << _a2 << endl;
		}
		cout << endl;





实现

‼️operator->必须返回指向数据的指针 ,而我之前犯错:返回的了数据本身(_node->_dataT类型,不是T*)。




实际上的访问应该是这样的:

cout << it.operator->()->_a1 << " : " << it.operator->()->_a1 << endl;

  1. it.operator->()返回对应的_data的指针,
  2. ->_a1相当于就是对结构体指针进行了"->"操作

但是编辑器省略了一个"->",只需要这样写就行:

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

cpp 复制代码
	///list_iterator 模板 
	template<class T>
	struct list_iterator
	{
		typedef list_node<T> Node;
		typedef list_iterator Self;
		Node* _node;
 
        
        
		T* operator->()/////////////////////////////////////////////////////////////////
		{
			return & _node->_data; ///operator->必须返回指向数据的指针

		}   
	};


..................
    
    
	struct AA
	{
		int _a1=1;
		int _a2=2;
	};


	void test_list1()
	{
		list<AA> lta;
		lta.push_back(AA());
		lta.push_back(AA());
		lta.push_back(AA());
		lta.push_back(AA());
		lta.push_back(AA());


		list<AA>::iterator it = lta.begin();

		while (it != lta.end())
		{
			///使用.的方法
			cout << (*it)._a1 << " : " << (*it)._a2 << endl;

			///使用->的方法:重载
			cout<< it->_a1 << " : " << it->_a2 << endl;
			++it;
		}
		cout << endl;
	}   





8.实现const_iterator

之前我们在实现vector中实现的print_container在这里用不了了,原因是没有实现const迭代器

cpp 复制代码
	template<class Container>
	void print_container(const Container& con)
	{
		//没有实例化,不是类型,过不了,得加typename或者用auto
		typename Container::const_iterator it = con.begin();
		while (it != con.end())
		{
			//*it += 10;

			cout << *it << " ";
			++it;
		}
		cout << endl;



		for (auto& e : con)
		{
			//e *= 10;
			cout << e << " ";
		}
		cout << endl;
	}





8.1为什么要实现const迭代器const_iterator而不是const iterator?

因为const修饰iterator,是iterator不能修改,

而const_iterator代表这个迭代器指向的内容不能修改






8.2如何实现?

最基础的理解:

------>对迭代器进行修改的list_iterator 类模板 中的成员函数,只有"解引用"和"箭头访问->"两个函数可能对迭代器指向的内容进行修改*

------>那只要修改这两个函数就行了:

cpp 复制代码
//原来的:
		T& operator*()
		{
			return _node->_data;

		}


		T* operator->()
		{
			return & _node->_data; ///注意  :返回的是T* 指针,不是T的引用
		}

//修改后的:

		const T& operator*()
		{
			return _node->_data;

		}


		const T* operator->()
		{
			return & _node->_data;  ///注意  :返回的是T* 指针,不是T的引用
		}

❌‼️但不能直接这样改,这样导致普通迭代器的解引用和箭头访问也失效了




8.3‼️两个模板类的版本实现const_iterator

✅实现两个不同的模板类,list_iterator 模板 类 和 list_const_iterator 模板 类

cpp 复制代码
	///list_iterator 模板 
	template<class T>
	struct list_iterator
	{
		typedef list_node<T> Node;
		typedef list_iterator Self;
		Node* _node;


		list_iterator(Node* node)
			:_node(node)
		{ }


		T& operator*()
		{
			return _node->_data;

		}


		T* operator->()
		{
			return & _node->_data; ///注意  :返回的是T* 指针,不是T的引用
		}

		Self&  operator++()
		{
			_node = _node->_next;
			return *this;
		}

		Self&  operator--()
		{
			_node = _node->_prev;
			return *this;
		}

		bool operator!=(const Self& s) const 
		{
			return _node != s._node;
		}

		bool operator==(const Self& s)const
		{
			return _node == s._node;
		}
	};







	///list_const_iterator 模板 
	template<class T>
	struct list_const_iterator
	{
		typedef list_node<T> Node;
		typedef list_const_iterator Self;
		Node* _node;


		list_const_iterator(Node* node)
			:_node(node)
		{ }


		const T& operator*()
		{
			return _node->_data;

		}


		const T* operator->()
		{
			return & _node->_data; ///注意  :返回的是T* 指针,不是T的引用
		}

		Self&  operator++()
		{
			_node = _node->_next;
			return *this;
		}

		Self&  operator--()
		{
			_node = _node->_prev;
			return *this;
		}

		bool operator!=(const Self& s) const 
		{
			return _node != s._node;
		}

		bool operator==(const Self& s)const
		{
			return _node == s._node;
		}
	};

当然,list模板中也要修改:

cpp 复制代码
	///list 模板
	template<class T>
	class list 
	{
		typedef list_node<T> Node;

	public:
		typedef list_iterator<T> iterator;
		typedef list_const_iterator<T> const_iterator;/////////////////////////////
        
         iterator begin()
		{
			iterator it(_head->_next);
				return it;
		}

		iterator end()
		{
			return _head;
		}
        
        
		const_iterator begin()const  ////////////////////////////////////////////////////////////
		{
			const_iterator  it(_head->_next);/////////////////////////////////////改iterator为const_iterator
				return it;
		} 

		const_iterator end()const//////////////////////////////////////////////////////////////
		{
			return _head;
		}



const的辨析:

const_iterator:看后面

const的位置 作用 核心规则
返回值类型前 修饰返回值本身,表示返回值不能被修改 ++只有返回引用 / 指针时才有意义++ ,返回值类型(如intbool)加了没用
函数参数前 修饰形参,表示函数内部不能修改这个形参 传类对象时必须加const&,既避免拷贝又保护实参
函数末尾 只能加在类的成员函数 后面,修饰this 指针 表示这个函数不能修改类的任何成员变量




✅ 形参的const

  • 形参是普通变量:void func(const int x) → x 在函数内部不能改
  • 形参是类对象:void func(const list<int>& lt) → lt 在函数内部不能改,且只能调用 lt 的 const 成员函数
  • 形参是指针:void func(const int* p)不能通过 p 修改指向的内容,(不能解引用修改和通过"->"修改)
操作 是否允许 原因
p = &b; ✅ 可以 指针本身可以指向其他地址
int a = *p; ✅ 可以 可以读取指向的内容
*p = 100; ❌ 不可以 不能通过 p 修改指向的内容
(*p)++; ❌ 不可以 不能通过 p 修改指向的内容
p++; ✅ 可以 指针本身可以移动



✅函数末尾的const

  • 普通成员函数的 this 指针是:T* const this → 指针本身不能改,指向的内容可以改
  • 末尾加 const 的成员函数的 this 指针是:const T* const this → 指针本身和指向的内容都不能改





8.5‼️彻底搞懂const_iterator

1.给谁用:

const 类对象

cpp 复制代码
// 普通对象 
list<int> lt;
list<int>::iterator it = lt.begin();
*it = 100; // ✅  

// const对象,不能修改
const list<int> clt = lt;
list<int>::iterator it = clt.begin(); // ❌  
list<int>::const_iterator cit = clt.begin(); // ✅  
*cit = 200; // ❌  const_iterator指向的内容不能改



2. 为什么有const_iterator

const对象不能修改,而普通迭代器可以访问并修改,故普通iterator不行‼️




3.const_iterator vs const iterator
类型 迭代器本身能不能改(++、--) 指向的内容能不能改(*it = x)
iterator ✅ 可以 ✅ 可以
const_iterator ✅ 可以 ❌ 不可以
const iterator ❌ 不可以 ✅ 可以
const const_iterator ❌ 不可以 ❌ 不可以



4. 为什么const_iteratoroperator*返回const T&,但函数末尾没加 const?

对象+const

  • 普通对象(非 const 对象)既可以调用 const 成员函数,也可以调用非 const 成员函数。
  • const 对象只能调用 const 成员函数
  • operator++会修改迭代器自己的_node成员,所以它绝对不能加 const。(不然const成员也能用了,不行)
  • 因为const_iterator也只是一种特殊的迭代器啊,实现的功能和iterator是相同的,都是++/--/*



5.函数末尾加 const 和返回值加 const 有没有必然联系?

没有‼️

但有一个注意点:

cpp 复制代码
class A
{
public: 
    int& get_a() const//❌
    {
        return _a;
    }
private:
    int _a = 10;
}; 

int main()
{
    const A a;
    a.get_a() = 20; // ❌ 竟然修改了const对象的成员!权限放大了
    return 0;
}
  • int& get_a()函数后面加了const,代表不论是普通对象还是const对象都能调用来得到_a的引用,自然也都能直接修改_a‼️,如果是const对象,这自然是错的

✅正确写法:

cpp 复制代码
const int& get_a() const
{
    return _a;
}










8.6‼️通过模板实现

因为两个iterator模板重复度太高,所以直接传不同的地方对应的内容到模板里面来生成不同的类

✅ **核心设计思想 **

  • 普通迭代器和 const 迭代器90% 的代码完全相同只有operator*operator->的返回值不同
  • 把这两个 "唯一不同的返回值类型" 抽成模板参数 RefPtr,放在类中对应处
  • 只写一套迭代器模板代码,通过传入不同的模板参数,生成两个完全不同的迭代器类



✅先来比较一下吧:

list模板
  • 原来的list模板
cpp 复制代码
	///list 模板
	template<class T>
	class list 
	{ 
        
	public:
		typedef list_iterator<T> iterator; 
         typedef list_const_iterator<T> const_iterator;
	};

  • 现在的list模板
    • 在 namespace 下写一个迭代器类模板struct list_iterator (只是模具,不是真正的类),然后在list类内部通过typedef传入不同参数,生成两个实体类
cpp 复制代码
	///list 模板
	template<class T>
	class list 
	{ 
        
	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;
 

	};





list_iterator 模板
  • 原来的list_iterator (和list_const_iterator 模板)模板
    • 在 namespace 下写两个独立的同级实体类模板list_iteratorlist_const_iterator(两份重复代码)
    • 实现了两个类模板,而大部分函数都一样,只有operator*()operator->()有区别

太长了就不写了,上面 "++8.3两个模板类的版本实现const_iterator++ "有写,唯三的区别在下面:

(其实构造函数也是一样的,只是生成对应的_node)

cpp 复制代码
	///list_iterator 模板 
	template<class T>
	struct list_iterator
	{ 

		➡️list_iterator(Node* node)
			:_node(node)
		{ }


		➡️T& operator*()
		{
			return _node->_data;

		}


		➡️T* operator->()
		{
			return & _node->_data; ///注意  :返回的是T* 指针,不是T的引用
		} 
	};







	///list_const_iterator 模板 
	template<class T>
	struct list_const_iterator
	{ 
		➡️list_const_iterator(Node* node)
			:_node(node)
		{ }


		➡️const T& operator*()
		{
			return _node->_data;

		}


		➡️const T* operator->()
		{
			return & _node->_data; ///注意  :返回的是T* 指针,不是T的引用
		}
 
	};
  • 现在的list_iterator 类模板:
    • 在 namespace 下写一个迭代器类模板struct list_iterator (只是模具,不是真正的类),然后在list类内部通过typedef传入不同参数,生成两个实体类
cpp 复制代码
	///list_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*()
		{
			return _node->_data;

		}


		➡️Ptr operator->()
		{
			return & _node->_data; ///注意  :返回的是T* 指针,不是T的引用
		}

		Self&  operator++()
		{
			_node = _node->_next;
			return *this;
		}

		Self&  operator--()
		{
			_node = _node->_prev;
			return *this;
		}

		bool operator!=(const Self& s) const 
		{
			return _node != s._node;
		}

		bool operator==(const Self& s)const
		{
			return _node == s._node;
		}
	};





生成过程:

list<int> lt;创造对象

------>实例化,T是int,编译器看到typedef list_iterator<int, int&, int*> iterator;typedef list_iterator<int, const int&, const int*> const_iterator;

------>代入迭代器模板 list_iterator<int, int&, int*>list_iterator<int, const int&, const int*>,生成两个类对象

------>typedef取别名,起了iteratorconst_iterator这两个短名字











9.erase:迭代器失效及修改

如果我想实现"删除偶数"这个功能,以下代码是错误的,因为erase之后it变成了野指针

❌:

cpp 复制代码
	void test_list1()
	{
		list<int> lt;
		lt.push_back(1);
		lt.push_back(2);
		lt.push_back(3);
		lt.push_back(4);
		lt.push_back(5);

		print_container(lt);


		auto it = lt.begin();
		while (it != lt.end())
		{
			if (*it % 2 == 0)
			{
				lt.erase(it);
			}
			++it;//此时it已经是野指针了

		}
        print_container(lt);


 
	}





原来的erase:

cpp 复制代码
		void erase(iterator pos)
		{
			assert(pos != end());///end是哨兵位头节点

			///迭代器只是一个类,想要访问对应的节点需要访问其中的_node成员变量
			Node* prev = pos._node->_prev;
			Node* next= pos._node->_next;

			prev->_next = next;
			next->_prev = prev;

			delete pos._node;
			--_size;
		}



修改后的erase:

注意点:

  1. erase是在list这个类中,返回值是迭代器,
cpp 复制代码
		iterator erase(iterator pos)
		{
			assert(pos != end());///end是哨兵位头节点 

			///迭代器只是一个类,想要访问对应的节点需要访问其中的_node成员变量
			Node* prev = pos._node->_prev;
			Node* next = pos._node->_next;// ✅ 这就是那个next!


			prev->_next = next;
			next->_prev = prev;

			delete pos._node;
			--_size;

			return next;
		}



对应的"删除偶数"函数:

cpp 复制代码
	void test_list1()
	{
		list<int> lt;
		lt.push_back(1);
		lt.push_back(2);
		lt.push_back(3);
		lt.push_back(4);
		lt.push_back(5);

		print_container(lt);


		auto it = lt.begin();
		while (it != lt.end())
		{
			if (*it % 2 == 0)
			{
				it= lt.erase(it);
			}
			else
			{
				++it;
			}

		}
		print_container(lt);

 
	}










10.更加规范的insert:

  • 返回值加上了
cpp 复制代码
		iterator insert(iterator pos, const T& x)
		{
			Node* cur = pos._node;///这里应该写成.而不是->,因为 pos是个类(结构体),_node是结构体的成员变量
			Node* prev = cur->_prev;

			Node* newnode = new Node(x);

			newnode->_next = cur;
			cur->_prev = newnode; 
			newnode->_prev = prev;
			prev->_next = newnode;

			++_size;

			return newnode;
		}










11.实现析构函数

  • 先实现clear,用来清除后面的所有数据
  • 然后在析构函数里实现哨兵位头节点的delete‼️,不要写成delete 了,因为这有这一个节点,而且list也不是连续存储的



clear函数:

  • 通过erase函数的遍历来实现除头节点的链表删除
  • 注意‼️:
    • erase是list的成员函数,不是iterator的成员函数
    • ++it; 多余且错误:erase返回值已指向next
cpp 复制代码
		void clear()
		{
			auto it = begin();
			while (it != end())
			{
				it =  erase(it); 
			}
            _size = 0; /// 补充:清空后重置size

		}




实现:

cpp 复制代码
	~list()
	{
		clear();
		delete _head; // 修正:delete[] → delete
		_head = nullptr;
	}





12.拷贝构造

一种特殊的构造函数,是构造函数的重载------符合构造函数的所有特点(函数名和类名相同,不准写void,可以重载,如果你不写编译器会自己造一个,类中有类的时候会调用内部类的拷贝构造函数)

  • 默认成员函数------用于创建时初始化
  • 传参
    • 第一个参数是自身类型的引用必须是引用,传值会报错()
    • 可以有其他参数,但其他参数必须有缺省值
  • 用处: 自定义类型的 ++传值传参++ 和++传值返回++都会调用拷贝构造
  • 你不写,编译器自己造的 拷贝构造函数 的行为:
    • 对内置类型(int ,char, int* ):只会进行浅拷贝 (一个字节一个字节 复制粘贴)
    • 对自定义成员变量(类中有类------两个Stack实现MyQueue):会调用内部的类 自己的拷贝构造函数
  • 什么时候不用你写:
    • 像是int,char这样的普通内置类型 ,你可以不写
    • 像是malloc/calloc/realloc出的 int*, char*,指向资源的内置类型,你得自己写
  • ------>传引用返回的好处:传值返回会进行拷贝构造,++传引用不需要拷贝构造++。
  • ------>传引用返回的弊端
    • 如果你引用的是一个局部变量,函数结束后就销毁,就有问题(解决方法:用static让局部变量变成全局变量以成功返回
    • 当然,如果是传值返回一个内置类型,不用操心



❌错误写法:

  • 拷贝构造也是构造,这时候list的

    private:

    Node* _head;

    size_t _size;

    都还没有初始化‼️

cpp 复制代码
		list(const list& lt)
		{ 
			for (auto& e : lt)
			{
				push_back(e);
			}
		}



✅正确写法:

cpp 复制代码
		list(const list& lt)
		{
			_head = new Node(T());
			_head->_next = _head;
			_head->_prev = _head;
			_size = 0; 

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








empty_init 的简洁写法:

下面的构造和拷贝构造看着有点重复度高,

cpp 复制代码
		list()
		{
			_head = new Node(T());
			_head->_next = _head;
			_head->_prev = _head;
			_size = 0; 
		}

		list(const list& lt)
		{
			_head = new Node(T());
			_head->_next = _head;
			_head->_prev = _head;
			_size = 0; 

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

所以简化重复部分到一个empty_init 函数中

cpp 复制代码
		void empty_init()
		{
			_head = new Node(T());
			_head->_next = _head;
			_head->_prev = _head;
			_size = 0;

		}

		list()
		{
			empty_init();
		}

		list(const list& lt)
		{
			empty_init();

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










13.赋值运算符重载

  • 默认成员函数------用于赋值

  • 是"=" 的运算符重载

特点:

  1. **有限制:**必须是 类的 成员函数,不能是全局函数
  2. **参数:**参数建议写成const + 当前类型的 引用(减少拷贝)
  3. return和返回值 :Date ,(return *this)(有返回值是为了支持连续赋值)。返回值也建议写成 当前类型的 引用Date& operator=(const Date& d)(提高效率)
  4. **编译器造的不靠谱:**你不写,编译器自动生成,也是浅拷贝(按字节拷贝,和拷贝构造函数一样,像是Stack,有malloc/calloc/realloc出的 int*, char ,指向资源的内置类型,你得自己写赋值运算符重载函数*)
  5. **类中有类:**对自定义成员变量(类中有类------两个Stack实现MyQueue):会调用内部的类 自己的 赋值运算符重载函数
  6. 啥时候自己写: 如果一个类显示实现了析构(~ / Destroy),你就也得写它的赋值运算符重载函数 。



实现:

  • 实现调换_head 和 _size的swap
  • 现代写法:
    • 传参的时候直接进行了拷贝构造,得到的是一个新的类对象 list<int> lt
    • 然后进行swap
    • lt 出了作用域销毁,调用析构函数,直接把原来this的对应内容销毁了,一举两得
cpp 复制代码
		void swap(list<T>& lt)
		{
			std::swap(_head, lt._head);
			std::swap(_size, lt._size);
		}

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










全部代码

cpp 复制代码
#pragma once
#include<assert.h>

#include<iostream>
using namespace std;


namespace lcj 
{



	 



	///list_node 模板
	template<class T>
	struct  list_node
	{
		T _data;
		list_node<T>* _prev;
		list_node<T>* _next;

		list_node(const T& data = T())
			:_data(data)
			,_next(nullptr)
			,_prev(nullptr)
		{ }


	};













	///list_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*()
		{
			return _node->_data;

		}


		Ptr operator->()
		{
			return & _node->_data; ///注意  :返回的是T* 指针,不是T的引用
		}

		Self&  operator++()
		{
			_node = _node->_next;
			return *this;
		}

		Self&  operator--()
		{
			_node = _node->_prev;
			return *this;
		}

		bool operator!=(const Self& s) const 
		{
			return _node != s._node;
		}

		bool operator==(const Self& s)const
		{
			return _node == s._node;
		}
	};







	/////list_const_iterator 模板 
	//template<class T>
	//struct list_const_iterator
	//{
	//	typedef list_node<T> Node;
	//	typedef list_const_iterator Self;
	//	Node* _node;


	//	list_const_iterator(Node* node)
	//		:_node(node)
	//	{ }


	//	const T& operator*()
	//	{
	//		return _node->_data;

	//	}


	//	const T* operator->()
	//	{
	//		return & _node->_data; ///注意  :返回的是T* 指针,不是T的引用
	//	}

	//	Self&  operator++()
	//	{
	//		_node = _node->_next;
	//		return *this;
	//	}

	//	Self&  operator--()
	//	{
	//		_node = _node->_prev;
	//		return *this;
	//	}

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

	//	bool operator==(const Self& s)const
	//	{
	//		return _node == s._node;
	//	}
	//};

















	///list 模板
	template<class T>
	class list 
	{
		typedef list_node<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()
		{
			iterator it(_head->_next);
				return it;
		}

		iterator end()
		{
			return _head;
		}

		const_iterator begin()const 
		{
			const_iterator  it(_head->_next);
				return it;
		}

		const_iterator end()const
		{
			return _head;
		}

		void empty_init()
		{
			_head = new Node(T());
			_head->_next = _head;
			_head->_prev = _head;
			_size = 0;

		}

		list()
		{
			empty_init();
		}

		list(const list& lt)
		{
			empty_init();

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



		~list()
		{
			clear();
			delete _head;
			_head = nullptr;
		}


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

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


		void clear()
		{
			auto it = begin();
			while (it != end())
			{
				it =  erase(it);
				///++it;多余且错误‼️
			}
			_size = 0; /// 补充:清空后重置size
		}

		//void push_back(const T& x)
		//{
		//	Node* newnode = new Node(x);


		//	Node* tail = _head->_prev; // 先拿到原来的尾节点

		//	newnode->_next = _head;    // 新节点的后继指向头节点
		//	newnode->_prev = tail;     // 新节点的前驱指向旧尾

		//	tail->_next = newnode;     // 旧尾的后继指向新节点
		//	_head->_prev = newnode;    // 头节点的前驱指向新节点  

		//	++_size;
		//}

		iterator insert(iterator pos, const T& x)
		{
			Node* cur = pos._node;///这里应该写成.而不是->,因为 pos是个类(结构体),_node是结构体的成员变量
			Node* prev = cur->_prev;

			Node* newnode = new Node(x);

			newnode->_next = cur;
			cur->_prev = newnode; 
			newnode->_prev = prev;
			prev->_next = newnode;

			++_size;

			return newnode;
		}

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

		void push_back(const T& x)
		{
			insert(end(), x);
		}
		 
		//void erase(iterator pos)
		//{
		//	assert(pos != end());///end是哨兵位头节点

		//	///迭代器只是一个类,想要访问对应的节点需要访问其中的_node成员变量
		//	Node* prev = pos._node->_prev;
		//	Node* next= pos._node->_next;

		//	prev->_next = next;
		//	next->_prev = prev;

		//	delete pos._node;
		//	--_size;
		//}

		iterator erase(iterator pos)
		{
			assert(pos != end());///end是哨兵位头节点 

			///迭代器只是一个类,想要访问对应的节点需要访问其中的_node成员变量
			Node* prev = pos._node->_prev;
			Node* next = pos._node->_next;///   这就是那个next!


			prev->_next = next;
			next->_prev = prev;

			delete pos._node;
			--_size;

			return next;
		}

		void pop_back()
		{
			erase(--end());
		}

		void pop_front()
		{
			erase(begin());
		}

		size_t size()
		{
			return _size;
		}

		bool empty() const
		{
			return _size == 0;
		}

	private:
		Node* _head;
		size_t _size;

	};
















	template<class Container>
	void print_container(const Container& con)
	{
		//没有实例化,不是类型,过不了,得加typename或者用auto
		typename Container::const_iterator it = con.begin();
		while (it != con.end())
		{
			//*it += 10;

			cout << *it << " ";
			++it;
		}
		cout << endl;



		for (auto& e : con)
		{
			//e *= 10;
			cout << e << " ";
		}
		cout << endl;
	}






















	 











	struct AA
	{
		int _a1=1;
		int _a2=2;
	};


	void test_list2()
	{
		list<AA> lta;
		lta.push_back(AA());
		lta.push_back(AA());
		lta.push_back(AA());
		lta.push_back(AA());
		lta.push_back(AA());


		list<AA>::iterator it = lta.begin();

		while (it != lta.end())
		{
			///使用.的方法
			//cout << (*it)._a1 << " : " << (*it)._a2 << endl;

			///使用->的方法:重载

			cout << it.operator->()->_a1 << " : " << it.operator->()->_a2 << endl;
			cout<< it->_a1 << " : " << it->_a2 << endl;
			++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();

	//	while (it != lt.end())
	//	{
	//		cout << *it << " ";
	//		++it;
	//	}
	//	cout << endl;
	//}	/// 测试 
 


	void test_list2()
	{
		list<int> lt;
		lt.push_back(1);
		lt.push_back(2);
		lt.push_back(3);
		lt.push_back(4);
		lt.push_back(5);

		print_container(lt);


		list<int> lt2(lt);
		print_container(lt);


		auto it = lt.begin();
		while (it != lt.end())
		{
			if (*it % 2 == 0)
			{
				it = lt.erase(it);
			}
			else
			{
				++it;
			}

		}
		print_container(lt);

		lt2 = lt;
		print_container(lt);

	}


	void test_list1()
	{ 
		
	}




}










更新代码

cpp 复制代码
#pragma once 
#include<assert.h>
#include<iostream>
using namespace std;

namespace lcj
{
    // List的节点类
    template<class T>
    struct list_node
    {
        list_node(const T& data = T())///初始化列表版本
            :_data(data)
            , _next(nullptr)
            , _prev(nullptr)
        {

        } 



        /// 缺省参数版本
        //T _data = T();
        //list_node<T>* _prev = nullptr;
        //list_node<T>* _next = nullptr;

        T _data ;
        list_node<T>* _prev ;
        list_node<T>* _next ;
    };


    // List的迭代器类
    template<class T, class Ref, class Ptr>
    struct list_iterator
    {
        typedef list_node<T> Node;
        typedef list_iterator<T, Ref, Ptr> Self;

        list_iterator(Node* node = nullptr)
            :_node(node)
        {
            //_node = node;///普通版本
        }

        Ref operator*()const
        {
            return   _node->_data ;
        }

        Ptr operator->()const
        {
            return &(_node->_data);///返回的是取地址后的指针
        }


        Self& operator++()
        {
            _node = _node->_next;
            return *this;
        }
        Self& operator--()
        {
            _node = _node->_prev;
            return *this; 
        }
        bool operator!=(const Self& s) const
        {
            return s._node != _node;
        }
        bool operator==(const Self& s) const
        {
            return s._node == _node;
        }

        Node* _node;
    };


    // list类
    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;

    public: 
        list()
        {
            empty_init();
        }

        list(const list<T>& lt)
        {
            empty_init();
            for (auto& e : lt)
            {
                push_back(e);
            }
        }

        list<T>& operator=(list<T> lt)  // 修正:原代码写的list<int>是错误的,应该是list<T>
        {
            swap(lt);
            return *this;
        }

        ~list()
        {
            clear();
            //erase(_head);
            delete _head;
            _head = nullptr;
        }


        iterator begin()
        {
            return _head->_next;///实际上是返回后进行了隐式类型转换(有对应的构造函数就行)
        }
        iterator end()
        {
            return _head;
        }
        const_iterator begin() const
        {
            return _head->_next;
        }
        const_iterator end() const
        {
            return _head ;
        }


        // List Capacity
        size_t size()
        {
            return _size;
        }
        bool empty() const
        {
            return _size == 0;
        }



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

        void pop_back()
        {
            //assert(end() != _head);
            assert(!empty());

            erase(_head->_prev);
        }

        void push_front(const T& x)
        {
            insert(_head->_next, x);
        }

        void pop_front()
        {
            //assert(end() != _head);
            assert(!empty());
            erase(begin());
        }

        // 在pos位置前插入值为x的节点,返回新节点的迭代器
        iterator insert(iterator pos, const T& x)
        {///  prev  newnode  pos

            ///这里错了,并不要改next,而是改"this"
            //Node* newnode = new Node(x);

            //Node* prev = pos._node->_prev;
            //Node* next = pos._node->_next;

            //newnode->_prev = prev;
            //newnode->_next = next;

            //prev->_next = newnode;
            //next->_prev = newnode;

            Node* newnode = new Node(x);

            Node* prev = pos._node->_prev;
            Node* pcur = pos._node;

            newnode->_prev = prev;
            newnode->_next = pcur;

            prev->_next = newnode;
            pcur->_prev = newnode;

            ++_size;

            return newnode;
        }




        // 删除pos位置的节点,返回该节点的下一个位置
        iterator erase(iterator pos)
        {
            assert(pos._node != _head);

            Node* prev = pos._node->_prev;
            Node* next = pos._node->_next;

            prev->_next = next;
            next->_prev = prev;

            delete pos._node;
            pos._node = nullptr;

            ///不要忘了改变_size
            --_size;

            return next;
        }
        void clear()
        {
            auto it = begin();
            while (it != end())
            {
                it=erase(it);///不要忘了改变it
            }

            ///不用改size,因为erase里面已经改过了
            //_size = 0;///这里不要忘了改_size
        }




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





    private:
        void empty_init()
        {
            _head = new Node(T());///传参,还是传参靠谱至少
            _head->_next = _head;///
            _head->_prev = _head;///

            _size = 0;
        }

        Node* _head = nullptr;
        size_t _size = 0;
    };


    // 通用打印函数
    template<class Container>
    void print_container(const Container& con)
    {
        for (auto& e : con)
        {
            cout << e << " ";
        }
        cout << endl;
    }


    // 测试用结构体
    //struct AA
    //{
    //    int _a1 = 1;
    //    int _a2 = 2;
    //};

    //// 测试函数
    //void test_list1();
    //void test_list2();


    //template<class Container>
    //void print_container(const Container& con)
    //{
    //    //没有实例化,不是类型,过不了,得加typename或者用auto
    //    typename Container::const_iterator it = con.begin();
    //    while (it != con.end())
    //    {
    //        //*it += 10;

    //        cout << *it << " ";
    //        ++it;
    //    }
    //    cout << endl;



    //    for (auto& e : con)
    //    {
    //        //e *= 10;
    //        cout << e << " ";
    //    }
    //    cout << endl;
    //}


































    struct AA
    {
        int _a1 = 1;
        int _a2 = 2;
    };


    void test_list3()
    {
        list<AA> lta;
        lta.push_back(AA());
        lta.push_back(AA());
        lta.push_back(AA());
        lta.push_back(AA());
        lta.push_back(AA());


        list<AA>::iterator it = lta.begin();

        while (it != lta.end())
        {
            ///使用.的方法
            //cout << (*it)._a1 << " : " << (*it)._a2 << endl;

            ///使用->的方法:重载

            cout << it.operator->()->_a1 << " : " << it.operator->()->_a2 << endl;
            cout << it->_a1 << " : " << it->_a2 << endl;
            ++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();

    //	while (it != lt.end())
    //	{
    //		cout << *it << " ";
    //		++it;
    //	}
    //	cout << endl;
    //}	/// 测试 



    void test_list2()
    {
        list<int> lt;
        lt.push_back(1);
        lt.push_back(2);
        lt.push_back(3);
        lt.push_back(4);
        lt.push_back(5);

        print_container(lt);


        list<int> lt2(lt);
        print_container(lt2);


        auto it = lt.begin();
        while (it != lt.end())
        {
            if (*it % 2 == 0)
            {
                it = lt.erase(it);
            }
            else
            {
                ++it;
            }

        }
        print_container(lt);

        lt2 = lt;
        print_container(lt2);

    } 





}
相关推荐
指尖的爷1 小时前
C++头文件的作用
开发语言·c++
Jabes.yang1 小时前
Java面试实录:AIGC场景下的Stream、微服务、Redis、Kafka与安全实战
java·spring boot·redis·微服务·面试·kafka·aigc
智者知已应修善业1 小时前
【51单片机0.1秒计时到21.0时点亮LED】2024-1-5
c++·经验分享·笔记·算法·51单片机
程序员二叉1 小时前
【Java】 面试核心合集:BigDecimal、缓存池、多态、反射全解析
java·缓存·面试
csdn_aspnet1 小时前
C# 使用linq给List某个属性值赋值
c#·list·linq
zh路西法1 小时前
【rosbridge-websocket】跨网络的ROS1与ROS2通讯法(上)
linux·网络·c++·python·websocket·网络协议
j7~1 小时前
【C++】类和对象(下)--详解之再探构造函数,友元,static成员,类型转换等
开发语言·c++·类型转换·友元·匿名对象·内部类·编译器优化
稷下元歌2 小时前
7天学会plc加机器视觉关于运动控制部份,配套视频在bib
开发语言·c++·git·vscode·python·docker·pip
薇茗2 小时前
【C++】 类与对象 基础篇
开发语言·c++·基础语法·类与对象