【C++】简单模拟实现vector模板

目录

一.前言

二.模板函数功能实现

1.vector构建

2.迭代器设置

3.构造函数和析构函数

①构造函数

②析构函数

4.size函数(获取大小)

5.capacity函数(获取容量)

6.push_back函数(尾插)

7.reserve函数(扩容)

8.pop_back函数(尾删)

9.resize函数(重定义大小)

10.swap函数(交换)

11.operator=(重载赋值)

12.operator[](重载接收)

13.insert(插入)与erase(删除)函数

①insert

②erase

三.源码(包含部分测试用例)


一.前言

        本文将通过模拟实现c++中的vector模板中一些常用的函数功能,具体包含push_back,reserve等函数的模拟,但并不考虑到所有的情况,因此并没有过多的函数重载。

        本文共两部分,一部分用来实现vector,头文件命名vector.h,另一部分则为源码实现部分(编译器环境为VS2019)。各个函数的返回值类型和参数类型均与库中的vector一样。

二.模板函数功能实现

1.vector构建

        vector类共有三个成员,不同于string类,因为vector需要适用各种模板类,且迭代器适用于所有模板,因此仅需要指向数据块的开始_start,指向有效数据的末尾_finish和指向存储容量的末尾_endOfStorage这三个迭代器即可。具体实现如下:

#pragma once

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

    private:
        iterator _start; // 指向数据块的开始
        iterator _finish; // 指向有效数据的尾
        iterator _endOfStorage; // 指向存储容量的尾
    };
}

2.迭代器设置

        因为vector类是数组,即连续存储的空间,可以直接通过加减等操作获得迭代器的位置。简单设置如下:

iterator begin()
{
    return _start;
}

iterator end()
{
    return _finish;
}

const_iterator begin() const
{
    return _start;
}

const_iterator end() const
{
    return _finish;
}

3.构造函数和析构函数

①构造函数

        此次模拟的构造函数共有四种,分别为默认构造函数、传值构造函数、拷贝构造函数和迭代器构造函数。

//默认构造
vector()
    :_start(nullptr)
    , _finish(nullptr)
    , _endOfStorage(nullptr)
{}

//传值构造
//此处n如果类型为size_t,会与下面迭代器构造函数引起int类型初始化的歧义
vector(int n, const T& value = T())
{
    T* tmp = new T[n];
    _start = tmp;
    _finish = _endOfStorage = tmp + n;
    for (size_t i = 0; i < n; ++i)
    {
        *tmp = value;
        tmp++;
    }
}

//迭代器构造
template<class InputIterator>
vector(InputIterator first, InputIterator last)
{
    while (first != last)
    {
        push_back(*first);
        first++;
    }
}

//拷贝构造
vector(const vector<T>& v)
{
    size_t sz = v.size();
    size_t cap = v.capacity();

    T* tmp = new T[cap];
    _start = tmp;
    _finish = tmp + sz;
    _endOfStorage = tmp + cap;
    for (size_t i = 0; i < sz; ++i)
    {
        *tmp = v[i];
        ++tmp;
    }
}

        vector的构造函数只需要设置好迭代器就行,如果开辟了新的空间,将迭代器设置过去就行,如果需要拷贝,也可以直接使用二者的迭代器拷贝。其中,上述的size()等函数都将在下面实现。

②析构函数

        析构函数很简单,由于vector是一段连续的数据块,直接释放掉起始位置即可,也就是迭代器初始位置_start。释放后将所有迭代器置空。

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

4.size函数(获取大小)

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

5.capacity函数(获取容量)

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

6.push_back函数(尾插)

        因为是连续的数据块,每次插入需要考虑扩容的问题,这里我们选择二倍扩容的方式。

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

    *_finish = x;
    _finish++;
}

7.reserve函数(扩容)

//扩容函数
void reserve(size_t n)
{
    if (n > capacity())
    {
        //防止迭代器失效,先保存原大小
        size_t Size = size();

        //拷贝数据到新的数据块
        T* tmp = new T[n];
        if (_start)
        {
            memcpy(tmp, _start, sizeof(T) * Size);
            delete[] _start;
        }

        //设置迭代器到新的数据块
        _start = tmp;
        _finish = tmp + Size;
        _endOfStorage = tmp + n;
    }
}

8.pop_back函数(尾删)

        尾删函数只需要将指向最后的迭代器向前移动一位即可,不用考虑容量大小的问题。(删除数据不需要减容,多此一举)

void pop_back()
{
    assert(_start);

    if (size())
    {
         _finish--;
    }
}

9.resize函数(重定义大小)

        该函数除了需要考虑扩容,还需要对数据块的大小做改变,重定义的大小为n,对超过原大小的数据使用传递的类初始化。

//重定义大小
void resize(size_t n, const T& value = T())
{
    reserve(n);
    while (n > size())
    {
        *_finish = value;
        _finish++;
    }
    _finish = _start + n;
}

10.swap函数(交换)

        从结构上看,vector的三个迭代器都是指针,swap直接交换二者的迭代器就可以,因此参数传引用即可。交换可以直接使用std中的交换函数。

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

11.operator=(重载赋值)

        因为前面有swap函数,要赋值的话直接把this指针指向类的迭代器与参数交换就可以了。但有个前提,参数类不能在函数结束时就被销毁,如果传引用则会改变原来的类,不适用;此时可以直接设置为传值调用——因为在调用时会对目标参数进行拷贝构造,此时的参数直接与*this交换即可。当然,如果使用传统的赋值也可以。

//赋值
vector<T>& operator=(vector<T> v)
{
    //size_t cap = v.capacity();
    //size_t sz = v.size();

    //T* tmp = new T[cap];
    //if (v.begin())
    //    memcpy(tmp, v._start, sizeof(T) * sz);
    //_start = tmp;
    //_finish = tmp + sz;
    //_endOfStorage = tmp + cap;


    swap(v);

    return *this;
}

12.operator[](重载接收)

T& operator[](size_t pos)
{
    return *(_start + pos);
}

const T& operator[](size_t pos) const
{
    return *(_start + pos);
}

13.insert(插入)与erase(删除)函数

        此处将二者放一起,因为二者都具有迭代器失效的可能性。

①insert

        在指定迭代器位置插入一个数据。具有扩容的可能性,但是我们传入的迭代器仍然指向原来的位置,此时已经释放掉了,因此我们此时需要对传入的迭代器重新设置。

        上述仅仅是解决了内部迭代器失效的问题,如果返回类型为void,外部的迭代器就会失效,无法继续访问,最好的解决办法就是返回更新后的迭代器,若要继续使用,请记住接收更新后的迭代器。

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

    if (_finish == _endOfStorage)
    {
        //防止迭代器失效
        size_t len = pos - begin();

        size_t new_cap = capacity() == 0 ? 4 : capacity() * 2;
        reserve(new_cap);

        pos = begin() + len;
    }

    iterator end = _finish - 1;
    while (end >= pos)
    {
        *(end + 1) = *end;
        end--;
    }

    _finish++;
    *pos = x;
    return pos + 1;
}

②erase

        删除指定迭代器位置的数据。删除的操作步骤有两个,一、删除迭代器位置的数据;二、挪动删除后面的数据到前面一位。但是系统自带的迭代器并不会因此连续访问同一个迭代器——它会不断++,直到end()。这意味着又需要我们手动来调整了,将返回值仍然设置为pos位置即可。

        倘若不返回pos位置,继续原本的迭代器,理论上是可行的,只会出现数据上的错误。其实不然,在不同的操作系统、环境下情况都不同,有的可以正常运行,而有的会报错,所以若要继续使用迭代器,一定要手动更新一下。

iterator erase(iterator pos)
{
    assert(pos >= begin() && pos < end());

    size_t beg = pos - begin();
    size_t sz = size();
    while (beg < sz)
    {
        _start[beg] = _start[beg + 1];
        beg++;
    }
    _finish--;

    return pos;
}

三.源码(包含部分测试用例)

#pragma once

#include <iostream>
#include <assert.h>

namespace Vector
{
    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 begin() const
        {
            return _start;
        }

        const_iterator end() const
        {
            return _finish;
        }

        //构造
        vector()
            :_start(nullptr)
            , _finish(nullptr)
            , _endOfStorage(nullptr)
        {}

        //此处n如果类型为size_t,会与下面类构造函数引起int类型初始化的歧义
        vector(int n, const T& value = T())
        {
            /*T* tmp = new T[n];
            _start = tmp;
            _finish = _endOfStorage = tmp + n;
            for (size_t i = 0; i < n; ++i)
            {
                *tmp = value;
                tmp++;
            }*/

            resize(n, value);
        }

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

        //拷贝构造
        vector(const vector<T>& v)
        {
            size_t sz = v.size();
            size_t cap = v.capacity();

            T* tmp = new T[cap];
            _start = tmp;
            _finish = tmp + sz;
            _endOfStorage = tmp + cap;
            for (size_t i = 0; i < sz; ++i)
            {
                *tmp = v[i];
                ++tmp;
            }
        }

        //赋值
        vector<T>& operator=(vector<T> v)
        {
            //size_t cap = v.capacity();
            //size_t sz = v.size();

            //T* tmp = new T[cap];
            //if (v.begin())
            //    memcpy(tmp, v._start, sizeof(T) * sz);
            //_start = tmp;
            //_finish = tmp + sz;
            //_endOfStorage = tmp + cap;

            swap(v);
            return *this;
        }

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

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

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

        //扩容函数
        void reserve(size_t n)
        {
            if (n > capacity())
            {
                //防止迭代器失效,先保存原大小
                size_t Size = size();
                T* tmp = new T[n];
                if (_start)
                {
                    memcpy(tmp, _start, sizeof(T) * Size);
                    delete[] _start;
                }
                _start = tmp;
                _finish = tmp + Size;
                _endOfStorage = tmp + n;
            }
        }

        //重定义大小
        void resize(size_t n, const T& value = T())
        {
            reserve(n);
            while (n > size())
            {
                *_finish = value;
                _finish++;
            }
            _finish = _start + n;
        }
        
        ///access///
        T& operator[](size_t pos)
        {
            return *(_start + pos);
        }

        const T& operator[](size_t pos) const
        {
            return *(_start + pos);
        }
        
        ///modify/
        void push_back(const T& x)
        {
            if (_finish == _endOfStorage)
            {
                size_t new_cap = capacity() == 0 ? 4 : capacity() * 2;
                reserve(new_cap);
            }

            *_finish = x;
            _finish++;
        }

        void pop_back()
        {
            assert(_start);

            if (size())
            {
                _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 >= begin() && pos <= end());

            if (_finish == _endOfStorage)
            {
                //防止迭代器失效
                size_t len = pos - begin();

                size_t new_cap = capacity() == 0 ? 4 : capacity() * 2;
                reserve(new_cap);

                pos = begin() + len;
            }

            iterator end = _finish - 1;
            while (end >= pos)
            {
                *(end + 1) = *end;
                end--;
            }
            
            _finish++;
            *pos = x;
            return pos + 1;
        }

        iterator erase(iterator pos)
        {
            assert(pos >= begin() && pos < end());

            size_t beg = pos - begin();
            size_t sz = size();
            while (beg < sz)
            {
                _start[beg] = _start[beg + 1];
                beg++;
            }
            _finish--;

            return pos;
        }

        void Test1()
        {
            push_back(1);
            push_back(2);
            push_back(3);
            push_back(4);
            push_back(5);
            push_back(5);
            push_back(5);
            push_back(5);

            pop_back();
            pop_back();
            pop_back();
        }

        void Test2()
        {
            reserve(20);
            std::cout << _endOfStorage - _start << std::endl;

            resize(10, 25);
            std::cout << _finish - _start << std::endl;
        }

        void Test3()
        {
            vector<T> a(5, 20);
            swap(a);

            *this = a;
            vector<T> copy(a);
        }

        void Test4()
        {
            insert(begin(), 1);
            insert(begin(), 1);
            insert(begin(), 1);
            insert(begin(), 1);

            erase(begin());
            erase(begin());
            erase(begin());
            erase(begin());
            erase(begin());

            erase(begin());

        }
    private:
        iterator _start; // 指向数据块的开始
        iterator _finish; // 指向有效数据的尾
        iterator _endOfStorage; // 指向存储容量的尾
    };
}

猜你喜欢

转载自blog.csdn.net/qq_74641564/article/details/131794357
今日推荐