【C++ 学习 ⑫】- 详解 vector 容器(下)- vector 容器的模拟实现及深度剖析

目录

一、vector.h

二、填充构造函数的重载

三、拷贝构造函数

四、迭代器失效问题



一、vector.h

【C++ 学习 ⑫】- 详解 vector 容器(下)- vector 容器的模拟实现及深度剖析_第1张图片

#pragma once
​
#include 
#include 
​
namespace yzz
{
    template
    class vector
    {
    public:
        typedef T* iterator;
        typedef const T* const_iterator;
​
        /*-------- 构造函数和析构函数 --------*/
        // 默认构造函数
        vector()
            : _start(0), _finish(0), _end_of_storage(0)
        { }
​
        // 填充构造函数
        vector(size_t n, const T& val = T())
            : _start(new T[n]), _finish(_start + n), _end_of_storage(_finish)
        {
            for (size_t i = 0; i < n; ++i)
            {
                _start[i] = val;
            }
        }
​
        // 填充构造函数的重载
        vector(int n, const T& val = T())
            : _start(new T[n]), _finish(_start + n), _end_of_storage(_finish)
        {
            for (size_t i = 0; i < n; ++i)
            {
                _start[i] = val;
            }
        }
​
        // 范围构造函数
        template
        vector(InputIterator first, InputIterator last)
            : _start(0), _finish(0), _end_of_storage(0)
        {
            while (first != last)
            {
                push_back(*first); 
                ++first;
            }
        }
​
        // 拷贝构造函数(实现深拷贝)
        vector(const vector& v)
            : _start(new T[v.capacity()]), 
              _finish(_start + v.size()),
              _end_of_storage(_start + v.capacity())
        {
            for (size_t i = 0; i < v.size(); ++i)
            {
                _start[i] = v._start[i];
            }
        }
​
        // 析构函数
        ~vector()
        {
            if (_start)
            {
                delete[] _start;
                _start = _finish = _end_of_storage = 0;
            }
        }
​
        /*-------- 赋值运算符重载(实现深拷贝) --------*/
        // 利用上面写好的拷贝构造函数实现深拷贝
        void swap(vector& v)
        {
            std::swap(_start, v._start);
            std::swap(_finish, v._finish);
            std::swap(_end_of_storage, v._end_of_storage);
        }
​
        vector& operator=(vector tmp)  
        {
            swap(tmp);
            return *this;
        }
​
        /*-------- 容量操作 --------*/
        size_t size() const
        {
            return _finish - _start;
        }
​
        size_t capacity() const
        {
            return _end_of_storage - _start;
        }
​
        bool empty()
        {
            return _start == _finish;
        }
​
        void reserve(size_t n)
        {
            if (n > capacity())
            {
                size_t sz = size();
                T* tmp = new T[n];
                if (_start)
                {
                    // 注意:此处与拷贝构造函数一样,不能使用 memcpy
                    for (size_t i = 0; i < sz; ++i)
                    {
                        tmp[i] = _start[i];
                    }
                    delete[] _start;
                }
                _start = tmp;
                // 异地扩容,因此需要更新 _finish 和 _end_of_storage
                _finish = _start + sz;
                _end_of_storage = _start + n;
            }
        }
​
        void resize(size_t n, const T& val = T())
        {
            if (n < size())
            {
                _finish = _start + n;
            }
            else  
            {
                reserve(n); 
                while (_finish != _start + n)
                {
                    *_finish = val;
                    ++_finish;
                }
            }
        }
​
        /*-------- 遍历及访问操作 --------*/
        T& operator[](size_t i)
        {
            assert(i < size());  // 前提是下标 i 合法
            return _start[i];
        }
​
        const T& operator[](size_t i) const
        {
            assert(i < size());  // 前提是下标 i 合法
            return _start[i];
        }
​
        iterator begin()
        {
            return _start;
        }
​
        const_iterator begin() const
        {
            return _start;
        }
​
        iterator end()
        {
            return _finish;
        }
​
        const_iterator end() const
        {
            return _finish;
        }
​
        /*-------- 修改操作 --------*/
        iterator insert(iterator pos, const T& val)
        {
            assert(pos >= _start && pos <= _finish);  // 前提是 pos 合法
            if (_finish == _end_of_storage)  // 考虑是否需要扩容
            {
                // 注意:因为是异地扩容,所以内部迭代器 pos 就会失效
                size_t sz = size();
                size_t old_capacity = capacity();
                reserve(old_capacity == 0 ? 1 : 2 * old_capacity);  // 2 倍扩容
                // 更新 pos
                pos = _start + sz;  
            }
            
            for (iterator end = _finish - 1; end >= pos; --end)
            {
                *(end + 1) = *end;
            }
            *pos = val;
            ++_finish;
            // 通过返回更新的 pos 来解决外部迭代器失效的问题。
            // 注意:不能通过 iterator& pos 的方法来解决外部迭代器失效的问题,
            // 因为对于类似于 v.insert(v.begin(), 100); 的语句,形参 pos 接收的是一个临时变量,
            // 临时变量具有常性,必须用常引用接收。
            return pos;
        }
​
        iterator erase(iterator pos)
        {
            assert(pos >= _start && pos < _finish);  // 前提是 pos 合法
            for (iterator it = pos + 1; it < _finish; ++it)
            {
                *(it - 1) = *it;
            }
            --_finish;
            // 假设是尾删操作,那么此时 pos == _finish,即内外部的迭代器都失效了
            return pos;
        }
​
        void push_back(const T& val)
        {
            //法一:
            //if (_finish == _end_of_storage)  // 首先考虑是否需要扩容
            //{
            //  size_t old_capacity = capacity();
            //  reserve(old_capacity == 0 ? 5 : 2 * old_capacity);
            //}
            //*_finish = val;
            //++_finish;
            
            //法二(复用):
            insert(end(), val);
        }
​
        void pop_back()
        {
            //法一:
            //assert(!empty());  // 前提是容器非空
            //--_finish;
​
            //法二(复用):
            erase(end() - 1);
        }
    private:
        iterator _start;
        iterator _finish;
        iterator _end_of_storage;
    };
}


二、填充构造函数的重载

表面上,提供了填充构造函数:

vector(size_t n, const T& val = T());

似乎就不再需要提供下面的重载了:

vector(int n, const T& val = T());

但实际上,对于下面这样的语句,就会出现问题,即:

yzz::vector v(5, 10);

【C++ 学习 ⑫】- 详解 vector 容器(下)- vector 容器的模拟实现及深度剖析_第2张图片

因为编译器在编译时,T 已经被实例化为 int 了,而 5 和 10 被编译器默认为 int 类型,所以如果不提供上面的填充构造函数的重载,编译器就会选择范围构造函数,而非填充构造函数,即

template
vector(InputIterator first, InputIterator last);

而当 InputIterator 被实例化为 int,当进行解引用操作时,就会出现错误


三、拷贝构造函数

拷贝构造函数的具体实现如下:

vector(const vector& v)
    : _start(new T[v.capacity()]), 
      _finish(_start + v.size()), 
      _end_of_storage(_start + v.capacity())
{
    for (size_t i = 0; i < v.size(); ++i)
    {
        _start[i] = v._start[i];
    }
}

注意:在拷贝构造函数的实现中,不能将 for 语句更改为下面的语句

memcpy(_start, v._start, sizeof(T) * v.size());

假设 T 被实例化为具有指针成员变量的类,例如 string 类,使用 memcpy 就会导致 vector 中的 string 类对象是通过浅拷贝,而非深拷贝构造的,与此同时会发生内存泄漏的问题。例如

yzz::vector v1;
v1.push_back("11111");
v1.push_back("22222");
v1.push_back("33333");
v1.push_back("44444");
v1.push_back("55555");
yzz::vector v2(v1);

【C++ 学习 ⑫】- 详解 vector 容器(下)- vector 容器的模拟实现及深度剖析_第3张图片

而通过 T 类中已实现深拷贝的赋值运算符重载就可以解决以上问题


四、迭代器失效问题

在 insert 及 erase 的实现中,涉及到了内外部迭代器失效的问题,而解决外部迭代器失效的办法就是在使用外部迭代器之前,通过返回值对迭代器重新赋值即可

例如,删除 vector 中所有的偶数

#include 
#include 
using namespace std;
​
int main()
{
    vector v{ 1, 2, 2, 2, 3, 4, 5, 6 };
    vector::iterator it = v.begin();
    while (it != v.end())
    {
        if (*it % 2 == 0)
            it = v.erase(it);  // 不能直接写 v.erase(it); 因为在 vs 中会直接报错
        else
            ++it;
    }
    
​
    for (auto e : v)
    {
        cout << e << " ";
    }
    // 1 3 5
    cout << endl;
    return 0;
}

你可能感兴趣的:(C++,c++,学习,开发语言)