Vector、List类简单介绍

vector和list跟我们学习数据结构中的顺序表和链表是相似的,这两个容器的接口和string类的也是有相似性的。在使用时,可以说使用方法基本一模一样。下面逐一介绍。

Vector篇

1.vector的使用

1.1构造函数

构造函数声明

功能说明

vector()

无参构造

vector(size_type n, const value_type& val = value_type())

构造并初始化n个val

vector(const vector& x)

拷贝构造

vector(InputIterator first, InputIterator last)

使用迭代器进行初始化构造

代码演示:

void TestVector()
{
    vector<int> v1;  //无参构造
    vector<int> v2(3, 0); //用3个0去初始化
    vector<int> v3(v2); //拷贝构造
    vector<int> v4(v3.begin(), v3.end()); //迭代器区间构造
}

1.2容量操作

函数名称

功能说明

size

获取数据个数

capacity

获取容量大小

empty

判断是否为空

resize

改变vector的size

reserve

改变vector的capacity

对于上述的函数说明和string是一样的,下面这篇文章中有介绍

String类 简单介绍_三分芝士的博客-CSDN博客

1.3迭代器

iterator

功能说明

begin + end

正向迭代器,begin是第一个数据的位置,end是最后一个元素的下一个位置

rbegin + rend

反向迭代器

代码演示:

void TestVector()
{
    vector<int> v1(10, 1);
    vector<int>::iterator vt = v1.begin();
    while (vt != v1.end())
    {
        cout << *vt << " ";
        ++vt;
    }
    cout << endl;
}

1.4修改操作

函数名称

功能说明

push_back

尾插

pop_back

尾删

insert

在pos之前插入val

erase

删除pos位置的数据

swap

交换两个vector的数据空间

operator[]

下标访问

代码演示:

void TestVector()
{
    vector<int> v1;
    v1.push_back(1);
    v1.push_back(2);
    v1.push_back(3);
    v1.push_back(4);  // 1234
    v1.pop_back();
    v1.pop_back(); // 12
    for (size_t i = 0; i < v1.size(); ++i)
    {
        cout << v1[i] << ' '; //1 2
    }
    cout << endl;
}

2.Vector模拟

2.1迭代器失效

(1)其实欲要模拟vector,给先要了解迭代器失效问题,在这里的由于我们的迭代器看作是原生指针T*,那么它失效则是原有的空间被销毁,而使用了这块被释放的空间,造成的后果就是程序崩溃。原有的空间为什么会销毁?在我们插入数据的过程中,如果原空间的大小已经不够存放数据,此时需要扩容,而vector的扩容大都是异地扩容,我们先来看看下面的插入代码:

void insert(iterator pos, const T& x)
{
    assert(pos < _finish);
    assert(pos >= _start);
    if (_finish == _endofstorage)
    {
        size_t newCapacity = capacity() == 0 ? 4 : capacity() * 2;
        reserve(newCapacity);
    }
    iterator end = _finish;
    while (end > pos)
    {
        *end = *(end - 1);
        --end;
    }
    *pos = x;
    ++_finish;
}

上述代码中由于扩容是异地扩容,会改变原指向的空间,此时pos 并不在这个空间范围内, 此时就会造成迭代器失效,因此可以先将我们的pos位置记下,利用元素之间个数记录, 后在将新空间的start + len 赋值给len

修改后的代码:

void insert(iterator pos, const T& x)
{
    assert(pos < _finish);
    assert(pos >= _start);
    if (_finish == _endofstorage)
    {
        size_t len = pos - _start;
        size_t newCapacity = capacity() == 0 ? 4 : capacity() * 2;
        reserve(newCapacity);
        pos = _start + len;
    }
    iterator end = _finish;
    while (end > pos)
    {
        *end = *(end - 1);
        --end;
    }
    *pos = x;
    ++_finish;
}

注意对于vector,可能其类型是string,或者其他类型,此时扩容就不能再像原来那样直接memcpy,memcpy是根据内存拷贝,但是vector<string>有两层需要拷贝,代码应是:

void reserve(size_t n)
        {
            if (n > capacity())
            {
                size_t Oldsize = size();
                iterator tmp = new T[n];
                if (_start)
                {
                    //用memcpy会发生浅拷贝的问题
                    //memcpy(tmp, _start, sizeof(T) * Oldsize);
                    for (size_t i = 0; i < oldSize; ++i)
                    {
                        tmp[i] = _start[i];
                    }
                    delete[] _start;
                }
                _start = tmp;
                _finish = tmp + Oldsize;
                _endofstorage = _start + n;
            }
        }

构造函数

//无参构造
vector()
    :_start(nullptr)
    , _finish(nullptr)
    , _endofstorage(nullptr)
{}

//这个地方用引用是为了减少拷贝,节省时间, 因为当val不是一个内置类型,而是一个自定义类型的时候
//此时拷贝的代价就会很大,因此用引用节省拷贝的开销
vector(int n, const T& val = T())
    :_start(nullptr)
    , _finish(nullptr)
    , _endofstorage(nullptr)
{
    reserve(n);
    for (size_t i = 0; i < n; ++i)
    {
        push_back(val);
    }
}
//迭代器区间构造
template<class Inputiterator>
        vector(Inputiterator first, Inputiterator last)
            :_start(nullptr)
            ,_finish(nullptr)
            ,_endofstorage(nullptr)
        {
            while (first != last)
            {
                push_back(*first);
                ++first;
            }
        }

拷贝构造,赋值重载

//传统写法1
vector(const vector<T>& v)
{
    _start = new T[v.size()];
    memcpy(_start, v._start, sizeof(T) * v.size());
    _finish = _start + v.size();
    _endofstorage = _start + v.size();
}
传统写法2
vector(const vector<T>& v)
    :_start(nullptr)
    , _finish(nullptr)
    , _endofstorage(nullptr)
{
    reserve(v.capacity());
    for (const auto& e : v)
    {
        push_back(e);
    }
}
//现代写法
vector(const vector<T>& v)
{
    vector<T> tmp(v.begin(), v.end());
    swap(tmp);
}

vector<T>& operator=(vector<T> v)
{
    swao(v);
    return *this;
}

析构函数

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

List篇

list的底层实际是双向带头循环链表,可以在常数范围内任意位置进行插入和删除的序列式容器。与其他容器相比list的最大缺陷是不支持任意位置的随机访问。

    • List的使用

1.1构造函数

构造函数

功能说明

list (size_type n, const value_type& val = value_type())

构造n个val的list

list()

构造空的list

list(const list& x)

拷贝构造函数

list(InputIterator first, InputIterator last)

用迭代器区间构造

代码演示:

void TestList()
{
    list<int> s1; //空构造
    list<int> s2(6, 1);  //1 1 1 1 1 1
    list<int> s3(s2.begin(), s2.end());  //1 1 1 1 1 1
    list<int> s4(s3);  // 1 1 1 1 1 1
}

1.2容量操作

函数声明

功能说明

empty

检测list是否为空

size

返回list中结点的有效个数

void TestList()
{
    list<int> s(6, 1);
    if (s.empty())
    {
        cout << "此list为空" << endl;
    }
    else
    {
        cout << s.size() << endl; //6
    }
}

1.3迭代器(重点)

函数声明

功能说明

begin + end

迭代器正向访问

rbegin + rend

迭代器反向访问

list的迭代器就不能是原生指针了,根据list的特性,它的物理空间不连续,结点与结点之间的物理地址也是不定的,此时如果对它++, --的话,会造成访问错误,所以我们要去封装一个迭代器来完成迭代器的++,--以及*等操作。

template <class T>
struct ListIterator
{
    typedef ListNode<T> node;
    node* _pnode;
    ListIterator(node* p)            
        :_pnode(p)
    {
    }
    T& operator*()
    {
        return _pnode->_data;
    }
    //前置++, --
    node& operator++()
    {
        _pnode = _pnode->_next;
        return *this;
    }
    node& operator--()
    {
        _pnode = _pnode->_prev;
        return *this;
    }
    node* _pnode;
};

我们发现这种写法对于const版本不行,如果我们直接在这个类中加上一个const的版本的话。

如:

T& operator*()
{
    return _pnode->_data;
}
//此时的const迭代器可以解引用,但是这个迭代器本身不能++
//因为const的修饰的是指针本身,而不是对象
const T& operator*() const 
{
    return _pnode->_data;
}

解决办法:

  1. 可以单独实现一个类,将上面的代码拷贝一个类出来,唯一区别就是加上了const

  1. 用多个模板参数来解决(最实用)

//此时const成员就传const,非const成员就传非const    
template <class T, class Ref>
    struct ListIterator
    {
        typedef ListNode<T> node;
        typedef ListIterator<T, Ref> Self;
        node* _pnode;
        ListIterator(node* p)       
            :_pnode(p)
        {
        }

        Ref operator*()
        {
            return _pnode->_data;
        }
        //前置++, --
        Self& operator++()
        {
            _pnode = _pnode->_next;
            return *this;
        }
        Self& operator--()
        {
            _pnode = _pnode->_prev;
            return *this;
        }
    };

1.4修改操作

函数说明

功能说明

push_front

在头部插入

push_back

在尾部插入

pop_front

在头部删除

pop_back

在尾部删除

insert

在pos前位置插入

erase

删除pos位置上的数据

swap

交换两个list的元素

clear

清空有效数据

List模拟

整体代码

namespace gwf
{
    template <class T> 
    struct ListNode
    {
        ListNode<T>* _next;
        ListNode<T>* _prev;
        T _data;

        ListNode(const T& x)
            : _next(nullptr)
            , _prev(nullptr)
            , _data(x)
        {
        }
    };

    template <class T, class Ref, class Ptr>
    struct ListIterator
    {
        typedef ListNode<T> node;
        typedef ListIterator<T, Ref, Ptr> Self;


        node* _pnode;

        ListIterator(node* p)
                
            :_pnode(p)
        {
        }

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

        //ptr 是指向此结构体的指针
        Ptr operator->()
        {
            return &_pnode->_data;
        }

        //前置++, --
        Self& operator++()
        {
            _pnode = _pnode->_next;
            return *this;
        }
        Self& operator--()
        {
            _pnode = _pnode->_prev;
            return *this;
        }

        //后置++, --
        Self& operator++(int)
        {
            Self tmp(*this);
            _pnode = _pnode->_next;
            return tmp;
        }
        Self& operator--(int)
        {
            Self tmp(*this);
            _pnode = _pnode->_prev;
            return tmp;
        }
        bool operator!=(const Self& it)
        {
            return _pnode != it._pnode;
        }
        bool operator==(const Self& it)
        {
            return _pnode == it._pnode;
        }
    };


    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 end()
        {
            return iterator(_head);
        }
        const_iterator begin() const
        {
            return const_iterator(_head->_next);
        }
        const_iterator end() const
        {
            return const_iterator(_head);
        }
        void emptyInitialize()
        {
            _head = new node(T());
            _head->_next = _head;
            _head->_prev = _head;
            _size = 0;
        }
        list()
        {
            emptyInitialize();
        }

        //这个地方提供两个是因为 当你使用list<int> lt1(4, 3);
        //此时编译器无法识别你是迭代器的区间类型还是什么size_t类型
        //因此提供一个int的类型可以快速识别
        list(int n, const T& val = T())
        {
            emptyInitialize();
            for (int i = 0; i < n; ++i)
            {
                push_back(val);
            }
        }
        list(size_t n, const T& val = T())
        {
            emptyInitialize();
            for (size_t i = 0; i < n; ++i)
            {
                push_back(val);
            }
        }
        template <class InputIterator>
        list(InputIterator first, InputIterator last)
        {
            emptyInitialize();
            while (first != last)
            {
                push_back(*first);
                ++first;
            }
        }
         传统写法
        ***********************************************************************************
        拷贝构造 lt2(lt1)
        //list(const list<T>& lt)
        //{
        //    emptyInitialize();
        //    for (auto& e : lt)
        //    {
        //        push_back(e);
        //    }
        //}

        赋值 lt2 = lt1
        //list<T>& operator=(const list<T>& lt)
        //{
        //    clear();
        //    for (auto& e : lt)
        //    {
        //        push_back(e);
        //    }
        //    return *this;
        //}
        ***********************************************************************************

        //现代写法

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

        }
        list(const list<T>& lt)
        {
            emptyInitialize();
            list<T> tmp(lt.begin(), lt.end());
            swap(tmp);
        }
        list<T>& operator=(list<T> lt)
        {
            swap(lt);
            return *this;
        }
        ~list()
        {
            clear();
            delete _head;
            _head = nullptr;
        }
        void clear()
        {
            iterator it = begin();
            while (it != end())
            {
                it = erase(it);
            }
        }
        void push_back(const T& x)
        {
            //node* newnode = new node(x);
            //node* tail = _head->_prev;

            //tail->_next = newnode;
            //newnode->_prev = tail;
            //newnode->_next = _head;
            //_head->_prev = newnode;
            //++_size;

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

        void pop_front()
        {
            erase(begin());
        }
        void pop_back()
        {
            erase(--end());
        }
        iterator insert(iterator pos, const T& x)
        {
            node* newnode = new node(x);
            node* cur = pos._pnode;
            node* prev = cur->_prev;
            prev->_next = newnode;
            newnode->_prev = prev;
            newnode->_next = cur;
            cur->_prev = newnode;
            ++_size;
            return iterator(newnode);
        }
        iterator erase(iterator pos)
        {
            assert(pos != end());
            node* prev = pos._pnode->_prev;
            node* next = pos._pnode->_next;
            prev->_next = next;
            next->_prev = prev;
            delete pos._pnode;

            --_size;
            return iterator(next);
        }

        size_t size() const
        {
            return _size;
        }
        bool empty() const
        {
            return _head->_next == _head->_prev;
        }


    private:
        node* _head;
        size_t _size;
    };
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值