C++STL---Vector、List所要掌握的基本知识

绪论​
拼着一切代价,奔你的前程。 ——巴尔扎克;本章主要围绕vector和list的使用,以及容器底层迭代器失效问题,同时会有对原码的分析和模拟实现其底层类函数。​​​​话不多说安全带系好,发车啦(建议电脑观看)。

附:红色,部分为重点部分;蓝颜色为需要记忆的部分(不是死记硬背哈,多敲);黑色加粗或者其余颜色为次重点;黑色为描述需要


1.Vector

vector 重要成员函数具体有
构造函数vetor()
析构函数~vector()
迭代器类函数begin()、end()、rbegin()、rend()、…
容量类函数resize()、size()、capacity()、reserve()、…
元素访问类函数operator[]、front()、back()、…
修改类函数push_back()、pop_back()、inser()、erase()、clear()、…
非成员函数swap()、relational operator()

——————————————

在这里插入图片描述Vector就像一个动态的数组也就是顺序表(有些时候可以想象成常规数组),也就是在写好的类中进行一定的函数操作,其中函数操作也就是上面所写。


1.1构造函数

函数作用函数原型
rang(迭代器)template <class InputIterator>vector (InputIterator first, InputIterator last,const allocator_type& alloc = allocator_type());
default(无参的全缺省的)explicit vector (const allocator_type& alloc = allocator_type());
fill(初始化vector为n个value值)explicit vector (size_type n, const value_type& val = value_type(),const allocator_type& alloc = allocator_type());
copy(拷贝构造)vector (const vector& x);
附:其中的const allocator_type& alloc = allocator_type())为空间配置器,这里提供了可以修改的缺省值,当你可以修改使用自己的内存池,基本不用修改STL内部提供的内存池
//初始化vector的方法
vector<int> v;//缺省
vector<int> v1(10,0);//初始化0个10
vector<int> v2(v1.begin(),v1.end());//迭代器从begin到end初始化
vector<int> v3(v2);//拷贝构造

1.2析构函数

在使用中不用去管因为其会自动执行释放构造函数所申请的空间


1.3迭代器类函数

知识点:

1.3.1begin()、end()、rbegin()、rend()

此处begin、end和string中的差不多用来指向结构中的数据,begin指向vector的首元素(类似于数组的首元素)、end指向的是数组最后一个元素的下一个位置,相反的rbegin指向的是数组的最后一个元素,而rend指向的是第一个元素的前一个位置无
其中可以发现begin指向vector内的数据,而end则不会指向vector内

练习使用:

	vector<int> v(10, 1);
	v.push_back(2);
	vector<int>::iterator it = v.begin();//指向vector首元素的1
	while (it != v.end())
	{
		cout << *it << ' ';
		it++;
	}
	//正向打印出所有元素:1 1 1 1 1 1 1 1 1 1 2 类似于指针
	cout << endl;
	vector<int>::reverse_iterator rit = v.rbegin();
	while (rit != v.rend())
	{
		cout << *rit << ' ';
		rit++;//注意此处仍然是++而不是--
	}
	//反向的打印出所有元素:2 1 1 1 1 1 1 1 1 1 1  类似于指针
	

附:cbegin、cend、crbeing、crend 是针对于const类型的,用法一样就不展开讲了。

1.4容量类函数

知识点:

1.4.1size()

函数原型:size_type size() const;
功能:查看vector中有几个元素。

vector<int> v(10,1);
v.size();//返回10

1.4.2capacity()

函数原型:size_type capacity() const;
功能:查看vector的capacity。

vector<int> v(10,1);
v.capacity();//在VS2019IDE环境下每次扩容1.5倍 1 ~ 2 ~ 3 ~ 6 ~ 9 ~ 13 ....

1.4.3reserve()

函数原型:void reserve (size_type n);
功能:设置vector的capacity,可提前把capacity设置好避免不断动态扩容的消耗

1.4.4resize()

函数原型:void resize (size_type n, value_type val = value_type());
改变vector的size(size决定了vector容器中的元素个数)

一般来说capacity申请扩展后都不会轻易的缩回

1.4.5empty()

函数原型:bool empty() const;
功能:查看vector内是否有数据,若返回true表示size == 0,否则放回false;

1.4.6shrink_to_fit()

函数原型:void shrink_to_fit();
功能:把capacity缩小到size大小(这条函数不一定执行最终是否执行还得看编译器他是无约束力的(non-binding))

1.5元素访问类函数

知识点:

1.5.1operator[]

函数原型
reference operator[] (size_type n);
const_reference operator[] (size_type n) const;

功能:像数组一样来访问vector中的元素
如:第一个元素v[0]

1.5.2at()

函数原型
reference at (size_type n);
const_reference at (size_type n) const;

功能:访问vector中的元素,但一般来说更多的用operator[]
如:v.at(0);访问第一个元素

1.5.3front()、back()

front函数原型back函数原型
reference front();reference back();
const_reference front() constreference back();

front的功能:找到vector中第一个元素并返回
如:vector<int> v(10,1); v.fornt();//指向向首元素1
back的功能:同理就是找到vector的最后一个元素
如: vector<int> v(10,1); v.back();//指向向最后一个元素1


写到这我觉得没必要再写了(有点浪费时间),对于这些函数可以自行查资料,里面有解释以及函数原型!c++官网,下面我将只写函数的用处,但仍然会把一些细节给写出来!


1.6修改类函数

作用具体函数
尾插push_back()
尾删pop_back()
清除数据clear()
将新的内容覆盖老的内容assign()
在某个位置删除数据erase()
在某处插入数据insert()
交换(直接交换两个对象所指向的地址)swap()

附:对于容器中我们若想在指定位置插入的话其实他并没有find函数来找到所对应的任意位置,对此在c++库中的算法库中其实有一个通用的find()查找函数
函数原型为:
template <class InputIterator, class T>
InputIterator find (InputIterator first, InputIterator last, const T& val);

用法如下:
vector<int> v;//在其中存储着1 2 3 4 5 6 7 find(v.begin(),v.end(),3);//传进迭代器区间,在其中找3,最后返回对应迭代器
这样就能找到容器中的数据并且进行一定的操作。


非成员函数
vector比较重载函数:relational operators
交换函数(用处是当两个是vector要进行交换时,去调用vector修改类中的swap,他是std交换函数的重载类):swap()

对于vector来说,还有个比较重要的点:

vector迭代器失效问题

在vector使用中可能会出现两种迭代器失效的情况:

  1. 当插入数据后的迭代器失效问题:因为在插入数据的情况下,有可能会发生异地扩容,当发生异地时就会导致原本迭代器所指向的位置为一个野指针也就是所说的迭代器失效,所以我们要注意的是迭代器在插入数据后要给迭代器重新赋值,并且在模拟实现插入函数中我们可能会扩容此处也要记得防止迭代器失效而记录迭代器位置,并作出一定的改变
        iterator insert(iterator pos, const T& x)
        {
            assert(pos <= _finish);
            assert(pos >= _start);

            if (_finish == _endofstorage)
            {
                size_t n = pos - _start;//记录相对位置防止迭代器在扩容后失效
                reserve(capacity() == 0 ? 4 : capacity() * 2);
                pos = _start + n;
            }
            iterator it = _finish;
            while (it > pos) 
            {
                *it = *(it - 1);
                it--;
            }
            *it = x;
            _finish++;

            return _start;
        }
  1. 当删除数据时我们要注意的是,他可能也会有迭代器失效的问题,因为在VS2019IDE下规定当一个迭代器所指向的位置被删除后,该迭代器就已经失效了,我们不能再次的去使用若使用就会报错,而在CentOs7 g++下迭代器会指向物理下的下一个位置(如:1234 删除2后3覆盖了2的位置迭代器就会指向4),此处解决的方法为:更新迭代器让它接收erase的返回值,在c++中规定了erase删除后返回迭代器指向被删除数据的下一个位置,这样才能正确的删除数据, 如:it = erase(it)//删除后需要接收返回的迭代器

vector深浅拷贝问题

当有异地扩容的时候,我们需要去将数据进行拷贝,此处如果是内置类型的话直接进行拷贝然后释放就行了,而对于像string类这种有深浅拷贝问题的就需要注意,我们若用memcpy直接进行的拷贝就会出深浅拷贝问题,对此我们在reserve扩容函数中就需要注意改变写法具体如下。

        void reserve(size_t n)
        {
            if (n  > capacity())
            {
                T* tmp = new T[n];
                size_t sz = size();
                if (_start) 
                {
                    //memcpy(tmp, _start, sz * sizeof(T)); 此处对于自定义类型来说是一个浅拷贝了
 
                    for(size_t i = 0; i < size();i++)
                    {
                        tmp[i] = _start[i];//逐个元素的进行赋值操作就能避免
                    }
                    delete[] _start;
                }
                _start = tmp;
                _finish = _start + sz;//这里不用size()因为这样会导致为nullptr , 
                _endofstorage = _start + n;
            }
        }

vector反向迭代器的实现

对于vector反向迭代器来说,我们不能像正向一样的直接用一个指针指向start,他们需要从后往前来走(也就表示当reverse_iterartor ++ 时并不是往前走的而是往后的),这样我们就需要重新分装一个反向迭代器的类,并且对操作符++、–重载改变其原理。对此在一个类中我们还需要去实现一些其他的操作符,具体如下:

    template<class iterator>
    class reverse_iterator
    {
        typedef reverse_iterator<iterator> Self;

        reverse_iterator(iterator t)
            :it(t)
        {}
        Self& operator++()
        {
            it--;
            return *this;
        }
        Self operator++(int)
        {
            Self tmp (it);
            it--;
            return tmp;
        }
        Self& operator--()
        {
            it++;
            return *this;
        }
        Self operator--(int)
        {
            reverse_iterator tmp = it;
            it++;
            return tmp;
        }

        Self& operator*()
        {
            reverse_iterator tmp = it;
            return *(--tmp);
        }

  

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

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

    private:
        iterator it;
    };

模拟实现Vector:

#pragma once
#include<iostream>
#include<stdio.h>
#include<assert.h>

using namespace std;

namespace bit
{
    template<class T>
    class vector
    {
    public:
        // Vector的迭代器是一个原生指针
        typedef T* iterator;

        typedef const T* const_iterator;

        typedef reverse_iterator<iterator> reverse_iterator;


        reverse_iterator rbegin()
        {
            return reverse_iterator(end());
        }

        reverse_iterator rend()
        {
            return  reverse_iterator(begin());
        }

        iterator begin() 
        {
            return _start;
        }

        iterator end() 
        {
            return _finish;
        }

        const_iterator cbegin() const
        {
            return _start;
        }

        const_iterator cend() const
        {
            return _finish;
        }

        // construct and destroy
        // 
        //对于直接的构造不做如何处理
        vector()
        {}

        vector(size_t n, const T& value = T())
        {
            reserve(n);
            while (n--) {
                push_back(value);
            }
        }

        vector(int n, const T& value = T())
        {
            reserve(n);
            while (n--) {
                push_back(value);
            }
        }

        template<class InputIterator>
        vector(InputIterator first, InputIterator last)
        {
            assert(first && last);
            while(first != last)
            {
                push_back(*first);
                first++;
            }
        }
        //现代写法:
        vector(const vector<T>& v)
        {
            vector<T> tmp(v.cbegin(),v.cend());

            swap(tmp);
        }
        //传统写法:
        //vector(const vector<T>& v)
        //{
        //    //开辟一个空间
        //    reserve(v.capacity());
        //    //把数据放进去
        //    for (auto& e : v)
        //    {
        //        push_back(e);
        //    }
        //}

        vector<T>& operator=(vector<T> v)
        {
            swap(v);
            return *this;
        }
        //void operator++(int)
        //{
        //    swap(v);
        //    return *this;
        //}

        ~vector()
        {
            delete[] _start;
            _start = _finish = _endofstorage = nullptr;
        }

         capacity

        size_t size() const
        {
            return _finish - _start;
        }

        size_t capacity() const
        {
            return _endofstorage - _start;
        }

        void reserve(size_t n)
        {
            if (n  > capacity())
            {
                T* tmp = new T[n];
                size_t sz = size();
                if (_start) 
                {
                    //memcpy(tmp, _start, sz * sizeof(T)); 此处对于自定义类型来说是一个浅拷贝了
 
                    for(size_t i = 0; i < size();i++)
                    {
                        tmp[i] = _start[i];
                    }
                    delete[] _start;
                }
                _start = tmp;
                _finish = _start + sz;//这里不用size()因为这样会导致为nullptr , 
                _endofstorage = _start + n;
            }
        }

        void resize(size_t n, const T& value = T())
        {
            if (n < capacity())
            {
                _finish = _start + n;
            }
            else {
                reserve(n);
                while (_finish < _start + n)
                {
                    *_finish = value;
                    ++_finish;
                }
            }
        }



        /access///

        T& operator[](size_t pos)
        {
            assert(pos < size());

            return _start[pos];
        }

        const T& operator[](size_t pos)const
        {
            assert(pos < size());

            return _start[pos];
        }



        /modify/

        void push_back(const T& x)
        {
   /*         if (_finish == _endofstorage)
            {
                int cp = capacity() == 0 ? 4 : capacity() * 2;
                reserve(cp);
            }
            *_finish = x;
            _finish++;*/
            insert(end(), x);
        }

        void pop_back()
        {
            _finish--;
        }

        void swap(vector<T>& v)
        {
            std::swap(_start, v._start);
            std::swap(_finish, v._finish);
            std::swap(_endofstorage, v._endofstorage);
        }

        iterator insert(iterator pos, const T& x)
        {
            assert(pos <= _finish);
            assert(pos >= _start);

            if (_finish == _endofstorage)
            {
                size_t n = pos - _start;//防止迭代器在扩容后失效
                reserve(capacity() == 0 ? 4 : capacity() * 2);
                pos = _start + n;
            }
            iterator it = _finish;
            while (it > pos) 
            {
                *it = *(it - 1);
                it--;
            }
            *it = x;
            _finish++;

  
            return _start;
        }

        iterator erase(iterator pos)
        {
            assert(pos < _finish);
            assert(pos >= _start);

            iterator it = pos + 1;
            while (it < _finish)
            {
                *(it - 1) = *it;
                it++;
            }
            
            _finish--;
            return pos;
        }
  

    private:
        //给他们初始值是为了预防
        iterator _start = nullptr; // 指向数据块的开始

        iterator _finish = nullptr; // 指向有效数据的尾

        iterator _endofstorage = nullptr; // 指向存储容量的尾

    };

    template<class iterator>
    class reverse_iterator
    {
        typedef reverse_iterator<iterator> Self;

        reverse_iterator(iterator t)
            :it(t)
        {}
        Self& operator++()
        {
            it--;
            return *this;
        }
        Self operator++(int)
        {
            Self tmp (it);
            it--;
            return tmp;
        }
        Self& operator--()
        {
            it++;
            return *this;
        }
        Self operator--(int)
        {
            reverse_iterator tmp = it;
            it++;
            return tmp;
        }

        Self& operator*()
        {
            reverse_iterator tmp = it;
            return *(--tmp);
        }

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

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

    private:
        iterator it;
    };

}



2.List

在STL的list其实就是数据结构中的带头双向循环列表(若不清楚的建议看看数据机构基础之链表)
在这里插入图片描述

list 重要成员函数具体有
构造函数list()
析构函数~list()
迭代器类函数begin()、end()、rbegin()、rend()、…
容量类函数size()、empty()、…
元素访问类函数front()、back()
修改类函数push_back()、pop_back()、push_front()、inser()、erase()、clear()、…
操作类函数sort() 、 reverse() 、remove() 、…
非成员函数swap()、relational operator()

同样的在list中也不会去详细的简述函数(这些都是熟能生巧的多去查文档),会在后面写道一些细节内容以及函数的功能

2.1构造函数、析构函数

构造函数( (constructor))作用
list (size_type n, const value_type& val = value_type())构造的list中包含n个值为val的元素
list()构造空的list
list (const list& x)拷贝构造函数
list (InputIterator first, InputIterator last)用[first, last)区间中的元素构造list

析构函数不用自己调用,其内部主要包括把所借的内存归还给操作系统操作

2.2迭代器类函数

具体函数作用
begin()返回第一个元素的迭代器
end()返回最后一个元素下一个位置的迭代器
rbegin()返回第一个元素的reverse_iterator,即end位置
rend返回最后一个元素下一个位置的reverse_iterator,即begin位置
注意点在反向迭代器中++,是表示往前走 而不是往后 ; 同理的-- 则表示往后走

2.3容量类函数

具体函数作用
empty()检测list是否为空,是返回true,否则返回false
size()返回list中有效节点的个数

2.4元素访问类函数

具体函数作用
front()返回list的第一个节点中值的引用
back()返回list的最后一个节点中值的引用

2.5修改类函数

具体函数作用
push_back(const value_type& val)在list尾部插入值为val的元素
pop_back()删除list中最后一个元素
push_front(const value_type& val)在list首元素前插入值为val的元素
pop_front()删除list中第一个元素
iterator inser(iterator position, const value_type& val)在pos位置(此处是迭代器)处插入值为val的元素,还能插入一次性n个元素以及用迭代器插入一段数据
iterator erase(iterator position)删除pos迭代器所指向位置的元素,还能一次性删除一段数据erase (iterator first, iterator last)
clear()清空list中的有效元素
swap(list& x))交换两个list中的元素
void resize (size_type n, value_type val = value_type())提前初始化n个空间初始值默认为value_type()//整形为0…

2.5.1list 删除操作时的迭代器失效问题:

list在插入数据时并不会迭代器失效,而在删除时因为当前位置的迭代器所指向的地址空间已经被释放,而形成了迭代器失效。
所以为避免这种情况删除的实现会返回一个迭代器(也就是被删除数据的下一个位置)并且我们需要用接收这个迭代器才能解决失效问题(例:iterator it = l.erase(l.begin()))
通过上面的分析我们能知道当list删除数据后其原本的迭代器会指向被删除的数据的下一个数据,并且返回该位置!对此来实现list的删除:

       iterator erase(iterator pos)
        {
            Node* cur = pos._Node;
            Node* prev = cur->_Prev;
            Node* next = cur->_Next;//一开始记录删除数据的下一个位置

            prev->_Next = next;
            next->_Prev = prev;

            _size--;
            return iterator(next);//返回next
        }

2.6操作类函数

具体函数作用
void splice (iterator position, list& x)拼接两个list,还能x指定中的i位置的一个元素拼接,或者是用迭代器确定区域(first,last]进行拼接
remove(valua)删除list中所有等于value值的元素
template <class Predicate> remove_if(Predicate pred)删除所有满足pred仿函数(也可能是函数指针)的元素
unique()删除重复的值
merge(list& x)拼接两个list,他有第二个参数为仿函数(或函数指针)
sort()对list中的数据进行排序,默认为升序,可以改变传递仿函数来自定义Compare比较函数(方法)
reverse()逆序把list中的数据倒过来

2.7list的实现

#pragma once

#include<iostream>
using namespace std;


namespace bite
{
    // List的节点类
    template<class T>
    struct ListNode
    {
        ListNode(const T& val = T())
            :_val(val)
            ,_Prev(nullptr)
            ,_Next(nullptr)
        {}

        ListNode<T>* _Prev;
        ListNode<T>* _Next;
        T _val;
    };

    //List的迭代器类
    template<class T, class Ref, class Ptr>
    struct ListIterator
    {
        typedef ListNode<T> Node;
        typedef ListIterator<T, Ref, Ptr> Self;
    public:
        ListIterator(Node* node = nullptr) 
            :_Node(node)
        {}

        //ListIterator(const Self& l);

        T& operator*()
        {
            return _Node->_val;
        }

        Ptr operator->()
        {
            return &_Node->_val;
        }

        Self& operator++()
        {
            _Node = _Node->_Next;
            return *this;
        }
      //后置
        Self operator++(int)
        {
            Self tmp(*this);//拷贝构造
            //其中self是类型、*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& l)
        {
            return _Node != l._Node;
        }

        bool operator==(const Self& l)
        {
            return _Node == l._Node;
        }

        Node* _Node;
    };

    template<class T, class Ref, class Ptr>
    struct RIterator
    {
        typedef ListNode<T> Node;
        typedef RIterator<T, Ref, Ptr> Self;
    public:
          RIterator(Node* node = nullptr)
            :_Node(node)
          {}

        //ListIterator(const Self& l);

        T& operator*()
        {
            return _Node->_val;
        }

        Ptr operator->()
        {
            return &_Node->_val;
        }

        Self& operator++()
        {
            _Node = _Node->_Prev;
            return *this;
        }
        //后置
        Self operator++(int)
        {
            Self tmp(*this);//拷贝构造
            //其中self是类型、*this是一个具体的对象
            _Node = _Node->_Prev;
            return tmp;
        }

        //前置
        Self& operator--()
        {
            _Node = _Node->_Next;
            return *this;
        }

        //后置需要一个类型来占位
        Self& operator--(int)
        {
            Self tmp(*this);
            _Node = _Node->_Next;
            return tmp;
        }

        bool operator!=(const Self& l)
        {
            return _Node != l._Node;
        }

        bool operator==(const Self& l)
        {
            return _Node == l._Node;
        }

        Node* _Node;
    };

    //list类
    template<class T>
    class list
    {
    public:
        typedef ListNode<T> Node;

        typedef ListIterator<T, T&, T*> iterator;//将


        typedef ListIterator<T, const T&, const T&> const_iterator;
    
        typedef RIterator<T, T&, T*> reverse_iterator;//将

    public:
        ///
        // List的构造

        void empty_init()
        {
            _Head = new Node;//头节点
            _Head->_Next = _Head;
            _Head->_Prev = _Head;
        }


        list()
        {
            empty_init();
        }

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

        template <class Iterator>
        list(Iterator first, Iterator last)
        {
            Iterator it = first;
            while (it != last)
            {
                push_back(*it);
                ++it;
            }
        }

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

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

        list<T>& operator=(list<T> l)
        {
            swap(l);

            return *this;
        }

        ~list()
        {
            clear();//将申请的空间返还
            delete _Head;
            _Head = nullptr;
        }

        ///
        // List Iterator
        iterator begin()
        {
            //return iterator(_Head->_Next);
            return _Head->_Next;

        }
        iterator end()
        {
            //return iterator(_Head);
            return _Head;
        }

        const_iterator begin()const
        {
            return _Head->_Next;
        }

        const_iterator end()const
        {
            return _Head;
        }
        
        reverse_iterator rbegin()
        {
            return reverse_iterator(_Head->_Prev);
        }

        reverse_iterator rend()
        {
            return reverse_iterator(_Head);
        }

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

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

         List Access
        T& front()
        {
            return _Head->_Next->_val;
        }

        const T& front()const
        {
            return _Head->_Next->_val;
        }
        //
        T& back()
        {
            return _Head->_Prev->_val;
        }
        //
        const T& back()const
        {
            return _Head->_Prev->_val;
        }
        
        // List Modify
        void push_back(const T& val)
        { 
            //Node* tail = _Head->_Prev;
            //Node* newnode = new Node(val);

            //tail->_Next = newnode;
            //newnode->_Prev = tail;

            //_Head->_Prev = newnode;
            //newnode->_Next = _Head;

            //_size++;
            insert(end(), val); 
        }


        void pop_back() 
        {
 /*           Node* tail = _Head->_Prev;

            tail->_Prev->_Next = _Head;
            _Head->_Prev = tail->_Prev;

            delete tail;

            _size--;*/
            erase(--end()); 
        }

        void push_front(const T& val) 
        { 
            //Node* head = _Head->_Next;
            //Node* newnode = new Node(val);

            //head->_Prev = newnode;
            //newnode->_Next = head;

            //_Head->_Next = newnode;
            //head->_Prev = _Head;

            //_size++;
            insert(begin(), val); 
        }

        void pop_front()
        { 
   /*         Node* head = _Head->_Next;

            head->_Next->_Prev = _Head;
            _Head->_Next = head->_Next;
            delete head;
            _size--;*/
            erase(begin());
        }
         在pos位置前插入值为val的节点
        iterator insert(iterator pos, const T& val)
        {
            Node* cur = pos._Node;
            Node* newnode = new Node(val);
            Node* prev = cur->_Prev;

            prev->_Next = newnode;
            newnode->_Prev = prev;

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

            prev->_Next = next;
            next->_Prev = prev;

            _size--;
            return iterator(next);
        }
        void clear()
        {
            iterator it = begin();
            while(it != end())
            {
                it = erase(it);
            }
            //for(auto it : )
            _size = 0;
        }
    private:
        Node* _Head;
        size_t _size;
    };
}

本章完。预知后事如何,暂听下回分解。

如果有任何问题欢迎讨论哈!

如果觉得这篇文章对你有所帮助的话点点赞吧!

持续更新大量C++细致内容,早关注不迷路。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/107763.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【Html】交通灯问题

效果 实现方式 计时器&#xff1a;setTimeout或setInterval来计时。setInterval和 setTimeout 在某些情况下可能会出现计时不准确的情况。这通常是由于JavaScript的事件循环机制和其他代码执行所需的时间造成的。 问询&#xff1a;通过getCurrentLight将每个状态的持续时间设置…

仿真软件Proteus8.10 SP3 pro一键安装、汉化教程(附proteus8.10下载链接安装视频)

本破解教程仅供个人及 proteus 8.10粉丝们交流学习之用&#xff0c;请勿用于商业用途&#xff0c; 谢谢支持。此版本为Proteus8.10 SP3 pro 这里写目录标题 安装包下载链接:视频教程 一、安装软件解压二、软件安装三、汉化 安装包下载链接: http://www.eemcu.cn/2022/05/14/pr…

vue3后台管理系统

项目创建及代码规范化开发 vue脚手架创建项目 安装vue脚手架 npm install-g vue/cli npm update -g vue/cli终端输入vue create 项目名称 即可进入模板选择 //利用vue-cli创建项目 进入模板选择 Vue CLI v5.0.8 ? Please pick a preset:Default ([Vue 3] babel, eslint)De…

Centos安装RabbitMQ,JavaSpring发送RabbitMQ延迟延时消息,JavaSpring消费RabbitMQ消息

1&#xff0c;版本说明 erlang 和 rabbitmq 版本说明 https://www.rabbitmq.com/which-erlang.html 确认需要安装的mq版本以及对应的erlang版本。 2&#xff0c;下载安装文件 RabbitMQ下载地址&#xff1a; https://packagecloud.io/rabbitmq/rabbitmq-server Erlang下载地…

exFAT文件系统的目录与文件存储

目录与文件存储的差异 在exFAT文件系统中&#xff0c;目录和文件的存储方式是不同的。 目录和文件都是以簇&#xff08;Cluster&#xff09;为单位进行存储&#xff0c;但它们的数据结构和用途不同。 目录的存储&#xff1a;目录&#xff08;子目录&#xff09;是用于组织和管…

Spring Boot进阶(93):体验式教程:手把手教你整合Spring Boot和Zipkin

&#x1f4e3;前言 分布式系统开发中&#xff0c;服务治理是一个比较重要的问题。为了更好地实现服务治理&#xff0c;需要解决服务跟踪问题&#xff0c;即如何对分布式系统中的服务进行监控和追踪。本文将介绍如何使用Zipkin进行服务跟踪&#xff0c;并结合Spring Boot进行整合…

解决cloudflare pages部署静态页面发生404错误的问题

cloudflare pages是一个非常方便的部署静态页面的sass工具。 但是很多人部署上去以后&#xff0c;访问服务会报404错误。什么原因&#xff1f; 原因如下图所示&#xff1a; 注意这个Build output directory, 这个是部署的关键&#xff01; 这个Build output directory目录的…

JVM性能优化 —— 类加载器,手动实现类的热加载

一、类加载的机制的层次结构 每个编写的”.java”拓展名类文件都存储着需要执行的程序逻辑&#xff0c;这些”.java”文件经过Java编译器编译成拓展名为”.class”的文件&#xff0c;”.class”文件中保存着Java代码经转换后的虚拟机指令&#xff0c;当需要使用某个类时&#…

虹科 | 解决方案 | 汽车示波器 索赔管理方案

索赔管理 Pico汽车示波器应用于主机厂/供应商与服务店/4S店的协作&#xff0c;实现产品索赔工作的高效管理&#xff1b;同时收集的故障波形数据&#xff0c;便于日后的产品优化和改进 故障记录 在索赔申请过程中&#xff0c;Pico汽车示波器的数据记录功能可以用于捕捉故障时的…

异步请求池——池式组件

前言 本文详细介绍异步请求池的实现过程&#xff0c;并使用DNS服务来测试异步请求池的性能。            两个必须牢记心中的概念&#xff1a; 同步&#xff1a;检测IO 与 读写IO 在同一个流程里异步&#xff1a;检测IO 与 读写IO 不在同一个流程 同步请求 与 异步请求…

聊聊“JVM 调优JVM 性能优化”是怎么个事?

所谓“调优”就是一个诊断和处理手段&#xff0c;最终的目标是让系统的处理能力&#xff0c;也就是“性能”达到最优化。 计算机系统中&#xff0c;性能相关的资源主要分为这几类&#xff1a; CPU&#xff1a;CPU 是系统最关键的计算资源&#xff0c;在单位时间内有限&#xf…

机器学习之查准率、查全率与F1

文章目录 查准率&#xff08;Precision&#xff09;&#xff1a;查全率&#xff08;Recall&#xff09;&#xff1a;F1分数&#xff08;F1 Score&#xff09;&#xff1a;实例P-R曲线F1度量python实现 查准率&#xff08;Precision&#xff09;&#xff1a; 定义&#xff1a; …

Unity中从3D模型资产中批量提取材质

如何使用 只需在“项目”窗口中创建一个名为“编辑器”的文件夹&#xff0c;然后在其中添加此脚本即可。然后&#xff0c;打开Window-Batch Extract Materials&#xff0c;配置参数并点击“ Extract&#xff01; ”。 在Unity 2019.1上&#xff0c;可以将默认材质重映射条件配…

RSA:基于小加密指数的攻击方式与思维技巧

目录 目录 目录 零、前言 一、小加密指数爆破 [FSCTF]RSA签到 思路&#xff1a; 二、基于小加密指数的有限域开根 [NCTF 2019]easyRSA 思路&#xff1a; 三、基于小加密指数的CRT [0CTF 2016] rsa 思路&#xff1a; 零、前言 最近&#xff0c;发现自己做题思路比较…

SpringCore完整学习教程5,入门级别

本章从第6章开始 6. JSON Spring Boot提供了三个JSON映射库的集成: Gson Jackson JSON-B Jackson是首选的和默认的库。 6.1. Jackson 为Jackson提供了自动配置&#xff0c;Jackson是spring-boot-starter-json的一部分。当Jackson在类路径上时&#xff0c;将自动配置Obj…

Java 将数据导出到Excel并发送到在线文档

一、需求 现将列表数据&#xff0c;导出到excel,并将文件发送到在线文档&#xff0c;摒弃了以往的直接在前端下载的老旧模式。 二、pom依赖 <!-- redission --><dependency><groupId>org.redisson</groupId><artifactId>redisson-spring-boot-…

UE5 C++自定义Http节点获得Header数据

一、新建C文件 选择All Classes&#xff0c;选择父类BlueprintFunctionLibrary&#xff0c;命名为SendHttpRequest。 添加Http支持 代理回调的参数使用DECLARE_DYNAMIC_DELEGATE_TwoParam定义&#xff0c;第一参数是代理类型&#xff0c;后面是参数1类型&#xff0c;参数1&…

APP自动化测试 ---- Appium介绍及运行原理

在面试APP自动化时&#xff0c;有的面试官可能会问Appium的运行原理&#xff0c;以下介绍Appium运行原理。 一、Appium介绍 1.Appium概念 Appium是一个开源测试自动化框架&#xff0c;可用于原生&#xff0c;混合和移动Web应用程序测试。它使用WebDriver协议驱动IOS&#xf…

33:深入浅出x86中断机制

背景 我们知道使用0x10号中断&#xff0c;可以在屏幕上打印一个字符。 问题 系统中的 中断 究竟是什么&#xff1f; 生活中的例子 来看一个生活中例子&#xff1a; 小狄的工作方式 在处理紧急事务的时候&#xff0c;不回应同事的技术求助。老板的召唤必须回应&#xff0c;…

C/C++面试常见问题——const关键字的作用和用法

首先我们需要一下const关键字的定义&#xff0c;const名叫常量限定符&#xff0c;当const修饰变量时&#xff0c;就是在告诉编译器该变量只可访问不可修改&#xff0c;而编译器对于被const修饰的变量有一个优化&#xff0c;编译器不会专门为其开辟空间&#xff0c;而是将变量名…