C++vector的模拟实现

一、vector接口总览

namespace bit

{

  template<class T>

  class vector

  {

  public:

    // Vector的迭代器是一个原生指针

    typedef T* iterator;

    typedef const T* const_iterator;

    iterator begin();

    iterator end();

    const_iterator cbegin();

    const_iterator cend() const;



    // construct and destroy

    vector();

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

    template<class InputIterator>

    vector(InputIterator first, InputIterator last);

    vector(const vector<T>& v);

    vector<T>& operator= (vector<T> v);

    ~vector();

    // capacity

    size_t size() const ;

    size_t capacity() const;

    void reserve(size_t n);

    void resize(size_t n, const T& value = T());



    ///access///

    T& operator[](size_t pos);

    const T& operator[](size_t pos)const;



    ///modify/

    void push_back(const T& x);

    void pop_back();

    void swap(vector<T>& v);

    iterator insert(iterator pos, const T& x);

    iterator erase(Iterator pos);

  private:

    iterator _start; // 指向数据块的开始

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

    iterator _endOfStorage; // 指向存储容量的尾

  };

}

 为了防止与标准库当中的vector区分,我们将自己模拟实现的vector放在命名空间bit中。

二、模拟实现vector

2.1 默认成员函数的模拟实现

2.1.1 构造函数1

 vector()
    :_start(nullptr)
	, _finish(nullptr)
	, _endofstorage(nullptr)
 {}

这是无参的构造函数,我们实现这个这个函数只需将三个成员变量设置成空指针即可。但C++支持成员变量设置缺省值,所以我们有更简洁的写法。

 vector()
 {}

 private:

     iterator _start = nullptr; // 指向数据块的开始

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

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

给成员变量一个缺省值空指针即可。

 2.1.2 构造函数2

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

这个构造函数可以使用n个value来初始化vector,我们先用reserve开好n个空间,最后将value依次尾插到开好的空间即可。

2.1.3 构造函数3

template<class InputIterator>
vector(InputIterator first, InputIterator last)
{
    while (first != last)
    {
        push_back(*first);
        ++first;
    }
}

这是用一段迭代器区间来对vector进行初始化,迭代器区间是其他容器的迭代器区间,对于其他容器的类型是不确定的,所以我们在这里要设计成函数模板,最后将每个数据依次尾插到容器中即可。

2.1.4 拷贝构造函数

vector(vector<T>& v)
{
    reserve(v.size());
    for (auto& x : v)
    {
        push_back(x);
    }
}

拷贝构造函数的实现也十分简单,首先先开好空间,再用范围for遍历容器,最后将数据依次尾插到新容器中即可。 

这里选需要注意深浅拷贝问题,如果使用memcpy函数进行拷贝,那么我们如果释放了旧空间,我们将找不到数据所在的空间了,因为memcpy是浅拷贝,两个容器实际上是共用一段空间,如果释放了旧空间,那么拷贝过后的容器也找不到数据,空间以及被释放掉了。

 2.1.5 赋值重载函数

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

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

赋值重载函数只需调用一个swap函数即可实现,而swap函数则是调用了标准库当中的swap函数,我们交换两个容器的值,那么左值与右值就交换了,便完成了赋值。

这里一样要注意深浅拷贝问题,一样不能使用memcpy进行拷贝,这种写法就是深拷贝。 

2.1.6 析构函数

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

析构函数也十分简单,我们首先先判断容器是否为空,如果不为空就将所开的空间释放掉,再将三个指针都置为空指针。

2.2 迭代器相关函数的实现

typedef T* iterator;

typedef const T* const_iterator;

vector的迭代器实际上就是一个原生指针,是当前数据类型的指针。

2.2.1 begin和end的模拟实现

 iterator begin()
 {
     return _start;
 }

 iterator end()
 {
     return _finish;
 }

begin返回容器的首地址,end返回容器当中有效数据的下一个位置。

2.3 容量和大小函数的模拟实现

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

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

size返回的是当前容器的数据个数,capacity返回的是当前容器的容量,我们用_finish-_start就可以得出数据个数,同样的用_endOfStorage - _start就可以得出当前容器的容量。

2.4 reserve函数的模拟实现

 template<class T>
 void bit::vector<T>::reserve(size_t n)
 {
     if (n > capacity())
     {
         size_t old_size = size();
         T* temp = new T[n];
         for (size_t i = 0; i < old_size; i++)
         {
             temp[i] = _start[i];
         }
         delete[] _start;
         _start = temp;
         _finish = _start + old_size;
         _endOfStorage = _start + n;
     }
 }

这里要注意reserve的扩容规则,如果n<capacity那么就不扩容,只有n>capacity的时候才进行扩容。 在扩容的时候我们要先将原先容器的长度记录下来,

 

在扩容完后,_start以及指向temp了,但此时的_finish还是指向原来容器的末尾,此时也要对_finish进行更新,而_start+old_size就是新容器_finish所指向的位置,所以在释放空间前要先记录原容器的长度,这样才能找到新容器中_finish所指向的准确位置,不对_finish进行更新,_finish-_size就会得出随机值,所得出的有效数据的个数就是错误的。

在这里一样要注意深浅拷贝的问题,我们一样不能够使用memcpy进行拷贝,否则释放旧空间时就找不到数据了。

 

2.5 resize函数的模拟实现

template<class T>
void bit::vector<T>::resize(size_t n, const T& value)
{
    if (n < size())
    {
        _finish = _start + n;
    }
    else
    {
        reserve(n);
        while (_finish < _start + n)
        {
            *_finish = value;
            ++_finish;
        }
    }
}

这里也要注意resize的规则,如果n<size则是进行删除,最终容器中只剩下n个数据,当n>size时就是进行尾插,尾插_start+n-_finish个数据。

void resize(size_t n, const T& value = T());

这是resize函数的声明, value给的缺省值T()主要是为了应对string未初始化的情况,当然C++的内置类型也有自己的默认构造函数,所以内置类型未初始化也可以应用这个缺省值。

2.6 empty函数的模拟实现

bool empty()const
{
    return _start == _finish;
}

empty可以判断该容器是否为空,我们只需判断_start和_finish两个指针的位置即可判断容器是否为空。

 

2.7 push_back函数的模拟实现

void push_back(const T& x)
{
    if (_finish == _endOfStorage)
    {
        reserve(capacity() == 0 ? 4 : 2 * capacity());
    }
    *_finish = x;
    ++_finish;
}

 首先判断容器的容量是否满了,满了需要扩容,没满就将数据尾插到_finish位置即可,最后让_finish++。

2.8 pop_back函数的模拟实现

void pop_back()
{
    assert(!empty());
    --_finish;
}

首先要判断容器是否为空,如果容器为空则不能进行尾删,不为空--_finish即可。

2.9 insert函数的模拟实现

template<class T>
T* bit::vector<T>::insert(iterator pos, const T& x)
{
    assert(pos >= _start);
    assert(pos < _finish);
    if (_finish == _endOfStorage)
    {
        size_t lenth = pos - _start;
        reserve(capacity() == 0 ? 4 : 2 * capacity());
        pos = _start + lenth;
    }
    iterator end = _finish + 1;
    while (pos <= end)
    {
        *end = *(end - 1);
        --end;
    }
    *pos = x;
    ++_finish;
    return pos;
}

首先判断位置是否合法,再判断是否需要扩容,在扩容时需要先记录原先容器pos和_start的相对位置,这样才能在扩容后找到要插入的位置,找到要插入的位置后,将pos及pos之后的位置全部往后移一位,最后在pos位置插入数据即可。

这里要注意的问题是,当你插入了一个数据并且还想使用pos位置的指针时,会达不到你想要的结果,这种情况是迭代器失效的问题,如果要想正确的使用pos位置的指针,就必须要对pos指针进行修改。

2.10 erase函数的模拟实现

template<class T>
T* bit::vector<T>::erase(iterator pos)
{
    assert(pos < _finish);
    assert(pos >= _start);
    assert(!empty());
    iterator it = pos + 1;
    while (it != end())
    {
        *(it - 1) = *it;
        ++it;
    }
    --_finish;
    return pos;
}

 erase可以删除pos位置的值,先判断位置是否合法以及容器是否为空,位置合法及容器不为空,就将pos位置之后的每一个数据依次往前挪一位,这样挪完位置之后pos位置的值便被覆盖掉了,完成删除。

2.11 operator[]函数的模拟实现

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

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

这是实现通过下标访问容器中的数据,我们只需检查下pos是否合法,合法返回pos所指向位置的值即可。

 

三、模拟实现vector完整代码

vector.h文件:

#pragma once
#include<iostream>
#include<assert.h>
using namespace std;

namespace bit

{

    template<class T>

    class vector

    {

    public:

        // Vector的迭代器是一个原生指针

        typedef T* iterator;

        typedef const T* const_iterator;

        iterator begin()
        {
            return _start;
        }

        iterator end()
        {
            return _finish;
        }

        const_iterator cbegin()
        {
            return _finish;
        }

        const_iterator cend() const
        {
            return _start;
        }



            // construct and destroy

        vector()
        {}

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

        template<class InputIterator>
        vector(InputIterator first, InputIterator last)
        {
            while (first != last)
            {
                push_back(*first);
                ++first;
            }
        }

        vector(vector<T>& v)
        {
            reserve(v.size());
            for (auto& x : v)
            {
                push_back(x);
            }
        }

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

        ~vector()
        {
            if (_start)
            {
                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);

        void resize(size_t n, const T& value = T());



            ///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)
            {
                reserve(capacity() == 0 ? 4 : 2 * capacity());
            }
            *_finish = x;
            ++_finish;
        }

        bool empty()const
        {
            return _start == _finish;
        }

        void pop_back()
        {
            assert(!empty());
            --_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);

        iterator erase(iterator pos);

    private:

        iterator _start = nullptr; // 指向数据块的开始

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

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

    };

    template<class T>
    void bit::vector<T>::reserve(size_t n)
    {
        if (n > capacity())
        {
            size_t old_size = size();
            T* temp = new T[n];
            for (size_t i = 0; i < old_size; i++)
            {
                temp[i] = _start[i];
            }
            delete[] _start;
            _start = temp;
            _finish = _start + old_size;
            _endOfStorage = _start + n;
        }
    }

    template<class T>
    void bit::vector<T>::resize(size_t n, const T& value)
    {
        if (n < size())
        {
            _finish = _start + n;
        }
        else
        {
            reserve(n);
            while (_finish < _start + n)
            {
                *_finish = value;
                ++_finish;
            }
        }
    }

    template<class T>
    T* bit::vector<T>::insert(iterator pos, const T& x)
    {
        assert(pos >= _start);
        assert(pos < _finish);
        if (_finish == _endOfStorage)
        {
            size_t lenth = pos - _start;
            reserve(capacity() == 0 ? 4 : 2 * capacity());
            pos = _start + lenth;
        }
        iterator end = _finish + 1;
        while (pos <= end)
        {
            *end = *(end - 1);
            --end;
        }
        *pos = x;
        ++_finish;
        return pos;
    }

    template<class T>
    T* bit::vector<T>::erase(iterator pos)
    {
        assert(pos < _finish);
        assert(pos >= _start);
        assert(!empty());
        iterator it = pos + 1;
        while (it != end())
        {
            *(it - 1) = *it;
            ++it;
        }
        --_finish;
        return pos;
    }

    template<class container>
    void print_container(container& v)
    {
        for (auto x : v)
        {
            cout << x << " ";
        }
        cout << endl;
    }

    void test_push_back()
    {
        vector<int> v;
        v.push_back(1);
        v.push_back(2);
        v.push_back(3);
        v.push_back(4);
        v.push_back(5);
        print_container(v);
    }

    void test_pop_back()
    {
        vector<int> v;
        v.push_back(1);
        v.push_back(2);
        v.push_back(3);
        v.push_back(4);
        v.push_back(5);
        print_container(v);

        v.pop_back();
        print_container(v);
    }

    void test_operator()
    {
        vector<int> v1;
        v1.push_back(1);
        v1.push_back(2);
        v1.push_back(3);
        v1.push_back(4);
        print_container(v1);

        vector<int> v2 = v1;
        print_container(v2);

        vector<int> v3;
        v3.push_back(10);
        v3.push_back(20);
        v3.push_back(30);

        v1 = v3;
        print_container(v1);
        print_container(v3);
        
        v3[2] = 40;
        print_container(v3);
    }

    void test_insert()
    {
        vector<int> v;
        v.push_back(1);
        v.push_back(2);
        v.push_back(3);
        v.push_back(4);
        v.push_back(5);

        print_container(v);

        int x;
        cin >> x;
        auto p = find(v.begin(), v.end(), x);
        if (p != v.end())
        {
           v.insert(p, 20);
        }
        print_container(v);
    }

    void test_erase()
    {
        vector<int> v;
        v.push_back(1);
        v.push_back(2);
        v.push_back(3);
        v.push_back(4);
        v.push_back(5);

        print_container(v);
        int x;
        cin >> x;
        auto p = find(v.begin(), v.end(), x);
        if (p != v.end())
        {
            v.erase(p);
        }
        print_container(v);
    }
}


test.cpp文件:

#include"vector.h"


int main()
{
	//bit::test_push_back();
	//bit::test_pop_back();
	//bit::test_operator();
	//bit::test_insert();
	bit::test_erase();
	return 0;
}

 

四、 总结

以上便是模拟实现vector函数接口的过程,希望以上所讲能够对大家有所帮助,有帮助的话,别忘了一键三连哦,感谢各位。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值