C++ STL容器之list的使用及复现

list

1. 序列式容器

vector、list、deque、forward_list(C++11 )等STL容器,其底层为线性序列的数据结构,里面存储的是元素本身,这样的容器被统称为序列式容器。

2. list容器

list 是用双向带哨兵位头节点的循环链表实现的。list 通过类模板来满足不同类型的数据,在使用时必须显示实例化调用。

3. list的成员函数

3.1 list的成员函数的介绍

函数声明 功能介绍
list< T > l() 构造一个空的list
list< T > l(n,val) 构造一个内容为n个val的list
list< T > l (l1.iterator1(),l1.iterator2()) 构造一个从l1的iterator1到iterator2的list
list< T >l(l1) 拷贝构造一个l1的list

2. list的迭代器

函数名 功能介绍
beginend begin:首元素的位置;end:下一个元素的位置
rbeginrend c指const,cbegin和cend指向的内容不能修改
cbegincend 反向迭代器,rbegin从end开始,rend从begin开始,其++和--的方向相反
crbegincrend 与前一个功能相同,但指向的内容不能修改

3.list的容量与元素访问

函数名 函数声明 功能介绍
size size_type size() const 返回list中有效元素个数
front reference front() const_reference front() const 返回list第一个元素的引用。
back reference back() const_reference back() const 返回list最后一个元素的引用。

4.list容器的修改

函数名 函数声明 功能介绍
size size_type size() const 返回list中有效元素个数
front reference front() const_reference front() const 返回list第一个元素的引用。
back reference back() const_reference back() const 返回list最后一个元素的引用。

4. list容器的重构

4.1 list迭代器的问题

迭代器的构造函数问题:

迭代器需要一个构造函数来初始化迭代器的状态,但迭代器不需要写拷贝构造,因为迭代器指向的节点不属于迭代器的类。

list迭代器的多参数特点:

在重载 list 的迭代器时,需要考虑普通迭代器的重载和 const 迭代器的重载。但如果只使用一个模板参数,就需要把普通迭代器的整段代码都拷贝下来单独做一份 const 迭代器的类,造成代码的冗余。(因为函数重载必须返回值相同,cons t修饰不算是重载,所以只能多做一个类)

stl中的list容器使用了多个模板参数来规避这个问题:

cpp 复制代码
template<class T, class Ref, class Ptr>
struct ListIterator
{
	typedef ListNode<T> Node;
	typedef ListIterator<T, Ref, Ptr> Self;
    
    Node* _node;
    
    ListIterator(Node* node)
        :_node(node)
        {}
    
    // *it
	//T& operator*()
	Ref operator*()
	{
		return _node->_data;
	}
		
	// it->
	//T* operator->()
	Ptr operator->()
	{
		return &_node->_data;
	}
    
	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;
	}

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

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

迭代器使用多个模板解决 const,本质相当于写了一个类模板,编译器生成了两个类。

list迭代器的比较:

注意 operator==operator!= 不是用 list 的值进行比较的,因为 list 中可以有多个相同的值,在迭代器遍历时会出现问题。实际它们的比较是通过地址来比较的,这样能确保在遍历的时候不会出问题。

4.2 begin和end

begin和end的const问题:

begin()end() 使用在 const 对象上时,不能直接对其进行 const 修饰。原因是对迭代器加 const 修饰,不能修改的是迭代器,但我们的目的是让迭代器指向的内容不能被修改,所以迭代器有 iteratorconst_iterator 两个版本。

cpp 复制代码
template<class T>
class list
{
	typedef ListNode<T> Node;
public:
	typedef ListIterator<T, T&, T*> iterator;
	typedef ListIterator<T, const T&, const T*> const_iterator;
    iterator begin()
	{
		return _head->_next;
	}
	 iterator end()
	{
		return _head;
	}
    
    const_iterator begin() const
	{
		return _head->_next;
	}
	 const_iterator end() const
	{
		return _head;
	}
}

begin和end的匿名对象问题:

在重构 list 的迭代器时候,begin()end() 的实现原理是匿名对象,而匿名对象会调用对象的拷贝构造:

cpp 复制代码
template<class T>
class list
{
	typedef ListNode<T> Node;
public:
	typedef ListIterator<T, T&, T*> iterator;
	typedef ListIterator<T, const T&, const T*> const_iterator;

	iterator begin()
	{
		return iterator(_head->_next);
    	 //也可以返回有名对象
    	//iterator it(_head->next);
    	//return it;
	}
	 iterator end()
	{
		return iterator(_head);
	}

}

甚至,可以把 iterator() 也省略掉,原理是单参数的构造函数支持隐式类型转换:

c++ 复制代码
template<class T>
class list
{
	typedef ListNode<T> Node;
public:
	typedef ListIterator<T, T&, T*> iterator;
	typedef ListIterator<T, const T&, const T*> const_iterator;

	iterator begin()
	{
		return _head->_next;
	}
	 iterator end()
	{
		return _head;
	}

}

4.3 operator->的重载

list容器对 operator-> 进行了重载,它的重构代码是:

c++ 复制代码
Ptr operator->()
{
	return &_node->_data;
}

在实际使用时,编译器为提高代码的可读性,还对其进行了优化:

c++ 复制代码
list<A>::iterator it = lt.begin();
while (it != lt.end())
{
	//*it += 10;
	//cout << (*it)._a1 << ":" << (*it)._a2 << endl;
	cout << it->_a1 << ":" << it->_a2 << endl;
	cout << it.operator->()->_a1 << ":" << it.operator->()->_a2 << endl;

	++it;
}

it->_a1 这段代码中,它的底层是 it.operator->()->_a1,按理它的调用应该是 it->->_a1,但编译器将第二个箭头拿掉,使得代码整体更好读。

4.4 完整代码

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


namespace mylist
{
    // List的节点类
    template<class T>
    struct ListNode
    {
        ListNode(const T& val = T())
            :_pPre(nullptr),_pNext(nullptr),_val(val)
        {}
        ListNode<T>* _pPre;
        ListNode<T>* _pNext;
        T _val;
    };


    //List的迭代器类
    template<class T, class Ref, class Ptr>
    class ListIterator
    {
        typedef ListNode<T>* PNode;
        typedef ListIterator<T, Ref, Ptr> Self;
    public:
        ListIterator(PNode pNode = nullptr)
            :_pNode(pNode)
        {}
        //ListIterator(const Self& l)
        //{};
        Ref& operator*()
        {
            return _pNode->_val;
        }
        Ptr operator->()
        {
            return &_pNode->_val;
        }
        Self& operator++()
        {
            _pNode = _pNode->_pNext;
            return *this;
        }
        Self operator++(int)
        {
            Self temp(*this);
            _pNode = _pNode->_pNext;
            return temp;
        }
        Self& operator--()
        {
            _pNode = _pNode->_pPre;
            return *this;
        }
        Self& operator--(int)
        {
            Self temp(*this);
            _pNode = _pNode->_pPre;
            return temp;
        }
        bool operator!=(const Self& l)
        {
            return _pNode != l._pNode;
        }
        bool operator==(const Self& l)
        {
            return _pNode == l._pNode;
        }
        PNode _pNode;
    };


    //list类
    template<class T>
    class list
    {
        typedef ListNode<T> Node;
        typedef Node* PNode;
    public:
        typedef ListIterator<T, T&, T*> iterator;
        typedef ListIterator<T, const T&, const T&> const_iterator;
    public:
        ///
        // List的构造
        list()
        {
            CreateHead();
        }

        list(int n, const T& value = T())
        {
            CreateHead();
            for (int i =0 ; i < n; i++)
            {
                push_back(value);
            }

        }
        template <class Iterator>
        list(Iterator first, Iterator last)
        {
            CreateHead();
            Iterator it = first;
            while (it != last)
            {
                push_back(*it);
                ++it;
            }
        }
        list(const list<T>& l)
        {
            CreateHead();
            const_iterator it = l.begin();
            while (it != l.end())
            {
                push_back(*it);
                ++it;
            }
        }
        list<T>& operator=(list<T> l)
        {
            swap(l);
            return *this;
        }
        ~list()
        {
            clear();
            delete _pHead;
            _size = 0;
        }


        ///
        // List Iterator
        iterator begin()
        {
            return _pHead->_pNext;
        }
        iterator end()
        {
            return _pHead;
        }
        const_iterator begin() const
        {
            return _pHead->_pNext;
        }
        const_iterator end() const
        {
            return _pHead;
        }


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


        
        // List Access
        T& front()
        {
            return *(_pHead->_pNext);
        }
        const T& front()const
        {
            return *(_pHead->_pNext);
        }
        T& back()
        {
            return *(_pHead->_pPre);
        }
        const T& back()const
        {
            return *(_pHead->_pPre);
        }


        
        // List Modify
        void push_back(const T& val) { insert(end(), val); }
        void pop_back() { erase(--end()); }
        void push_front(const T& val) { insert(begin(), val); }
        void pop_front() { erase(begin()); }
        // 在pos位置前插入值为val的节点
        iterator insert(iterator pos, const T& val)
        {
            Node* cur = pos._pNode;
            Node* newnode = new Node(val);
            newnode->_pNext = cur;
            newnode->_pPre = cur->_pPre;

            cur->_pPre->_pNext = newnode;
            cur->_pPre = newnode;
            _size++;
            return iterator(newnode);
        }
        // 删除pos位置的节点,返回该节点的下一个位置
        iterator erase(iterator pos)
        {
            Node* cur = pos._pNode;
            Node* prev = cur->_pPre;
            Node* next = cur->_pNext;

            prev->_pNext = next;
            next->_pPre = prev;

            delete cur;
            _size--;
            return iterator(next);
        }
        void clear()
        {
            iterator it = begin();
            while (it != end())
            {
                it = erase(it);
            }
        }
        void swap(list<T>& l)
        {
            std::swap(_pHead, l._pHead);
            std::swap(_size, l._size);
        }
    private:
        void CreateHead()
        {
            _pHead = new Node;
            _pHead->_val = 0;
            _pHead->_pNext = _pHead;
            _pHead->_pPre = _pHead;
        }
        PNode _pHead;
        size_t _size=0;
    };
};
相关推荐
@_@哆啦A梦2 分钟前
windows怎样查看系统信息(处理器等)
windows
JANGHIGH12 分钟前
c++ std::list使用笔记
c++·笔记·list
画个逗号给明天"19 分钟前
C++STL容器之list
开发语言·c++
干炒 牛河25 分钟前
数据结构:双链表list
数据结构·list
Lqingyyyy2 小时前
P2865 [USACO06NOV] Roadblocks G 与最短路的路径可重复的严格次短路
开发语言·c++·算法
C语言小火车2 小时前
深入解析C++26 Execution Domain:设计原理与实战应用
java·开发语言·c++·异构计算调度·c++26执行模型·domain定制
落落落sss2 小时前
MongoDB
数据库·windows·redis·mongodb·微服务·wpf
转身後 默落3 小时前
04.Docker 镜像命令
docker·容器·eureka
ox00803 小时前
C++ 设计模式-中介者模式
c++·设计模式·中介者模式
黄铎彦3 小时前
使用GDI+、文件和目录和打印API,批量将图片按文件名分组打包成PDF
c++·windows·pdf