vector的实现

类的成员变量

namespace lty{
    template <class T>
    class vector
    {
    public:
        typedef T* iterator;
        typedef const T* const_iterator;

		private:
        iterator _start = nullptr;
        iterator _finish = nullptr;
        iterator _end_of_storage = nullptr;
}

公有成员变量:

重命名模版类型T*指针为迭代器iterator和const类型的const_iterator迭代器

私有成员变量:

_start为vector的首地址;

_finish为尾地址,注意,_finish中存放的数据应被屏蔽,无法使用,可以理解为字符串中的 ’\0’ 字符,遇到则结束;

_end_of_storage为内存最大地址,即这片空间末尾的地址,一般扩容时会修改这个成员的大小。

Tip:C++11后支持私有成员缺省赋值,可以不必在构造函数中初始化。

capacity()和size()

基本的函数

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

capacity()函数返回空间大小,空间大小用最大空间地址_end_of_storage减去起始地址_start即可。

size()函数返回数组数据的数量,存放的数据到_finish结束,用_finish减去起始地址_start即可。

operator[ ]重载

T& operator[](size_t pos)
        {
            assert(pos<size());
            return _start[pos];
        }
const T& operator[](size_t pos) const//函数重载 给const类型用
        {
            assert(pos<size());
            return _start[pos];
        }

第一个重载用于普通数据,重载需要的参数为pos,即数组的下标位置,断言防止越界访问,返回的值是_start[pos],_start本身就是一个数组,可以用中括号[ ]访问数据。

第二个重载是专门给const类型使用,有时候数组中的数据如_start[0]需要传给一个函数,此时希望数组中的数据不被改变,需要加上const,这时const类型就能调用重载了。前面的const是用来限制返回值不被修改,后面的const是限制隐式参数this不被修改。

Tip:const成员调用非const函数是权限放大,无法调用,普通用户调用const是权限缩小

swap交换:

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

利用std库中的swap交换所有成员变量。

打印

void Print(const vector<int> v)
    {
        for(size_t i=0;i<v.size();++i)
        {
            cout << v[i] << " ";
        }
        cout << endl;
        auto it=v.begin();
        while(it<v.end())
        {
            cout << *it << " ";
            ++it;
        }
        cout << endl;
        for(auto a:v)
        {
            cout << a << " ";
        }
        cout << endl;
    }

当实现了迭代器iterator和operator[ ]重载后,我们就可以用v[i]和迭代器打印vector中的数据,当然自动遍历也因为底层和迭代器有关也可以使用。

构造函数初始化列表、析构、拷贝构造

初始化构造

无参构造:

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

初始化三个私有成员变量都为nullptr。

Tip:C++11后支持私有成员缺省赋值,可以不必在列表中初始化。

有参构造:

			//vector(size_t n,const T& val=T()) //两个都写编译器会自动匹配
				vector(int n,const T& val=T())
                :_start(nullptr)
                ,_finish(nullptr)
                ,_end_of_storage(nullptr)
        {
            reserve(n);
            for(size_t i=0;i<n;i++)
            {
                push_back(val);
            }
        }

先初始化三个私有成员变量后,再进行扩容,然后一次插入val值,完成n个val值的构造。

Tip:原本形参n是size_t类型,但是当传入的数据为int型时,会发生隐式转换,编译器会优先匹配下面的模版类型的拷贝构造,从而出错。

所以这里可以写两个一样,但形参n类型不一样的析构函数。

深拷贝构造:

传入迭代器来构造:

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

传入两个迭代器容器,用循环来依次把迭代器中的数据插入到迭代器指向位置。

v2(v1)拷贝构造:

vector(const vector<T>& v)
        {
            _start = new T[v.capacity()];
            //memcpy(_start, v._start, sizeof(T)*v.size());//浅拷贝
            for (size_t i = 0; i < v.size(); ++i)
            {
                _start[i] = v._start[i];//vector<vector<int>>时,没写赋值重载,在这个地方是一个浅拷贝,需要再写一个赋值重载
            }

            _finish = _start + v.size();
            _end_of_storage = _start + v.capacity();
        }

当出现vector<vector>时是个二维数组,系统默认生成的赋值重载是第一个vector中的vector的地址浅拷贝,如下图:

在这里插入图片描述
所以我们需要写一个赋值重载来实现二维的深拷贝:

vector<T>& operator=(vector<T> v)
        {
            swap(v);//this和形参v交换,交换后的v因为是形参所以不会改变
            return *this;
        }

利用swap把形参v的数据交换过去,再返回*this即可。

我们写完赋值重载后,可以配合前面的“迭代器拷贝”来简单复写v2(v1)拷贝构造:

vector(const vector<T>& v)
        {
            vector<T> tmp(v.begin(),v.end());//借助迭代器拷贝来把v中的数据全部拷贝进tmp中
            swap(tmp);//tmp和this交换数据即可实现二维v2(v1)深拷贝
        }

借助迭代器拷贝把拷贝数据全部放入tmp中,再用swap函数直接把tmp中的值交换进*this中。

析构函数:

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

析构函数没什么好说的,直接删除_start中数据,其余指针置nullptr即可。

迭代器实现

				iterator begin()
        {
            return _start;
        }
        iterator end()
        {
            return _finish;
        }
        const_iterator begin() const
        {
            return _start;
        }
        const_iterator end() const
        {
            return _finish;
        }

分为两种迭代器,一种是可以改变指向数据值的迭代器;一种是const类型迭代器,无法改变值。

reserve和resize两种扩容

Tip: 这两种扩容都不可以缩容

reserve扩容:

				void reserve(size_t n)
        {
            if(n>capacity())//判断是否缩容
            {
                size_t sz=size();//储存扩容前的size大小
                T* tmp=new T[n];
                if(_start)//如果不为空
                {
                    //memcpy(tmp,_start,sizeof(T)*size());
										for (size_t i = 0; i < sz; ++i)
                    {
                        tmp[i]=_start[i];
                    }
                    delete[] _start;
                }
                _start=tmp;//此时_start地址已改变,直接用size()会导致_finish位置出错
                _finish=_start + sz;//修改_finish正确指向
        //_finish=_start + size();//_finish - _start 抵消剩_finish,且size()为负数
                _end_of_storage=_start+n;
            }
        }

reserve扩容需要传入一个扩容大小的值n,当n比原来的容量大时才进行扩容操作,且reserve不接受缩容操作。

扩容不是原地扩容,函数中扩容借助了tmp的帮助来拷贝_start中的数据,然后改变_start指向来完成深拷贝。但是此时_start指向已经改变,和_finish的指向不匹配,不是一块空间中的地址,所以使用前面的sz来存储扩容前的有效数据大小,再和_start相加,即可得到正确的vector尾地址**_finish**。

**Tip:**这里本来用的是memcpy数据扩容,但是当vector中的数据为指针类型的例如string类型,memcpy是一种浅拷贝,所以这里采用for循环去依次赋值。

到此扩容才完整结束。

resize扩容:

void resize(size_t n,T val=T())
        {
            if(n<size())
            {
                _finish=_start+n;//删除数据
            }
            else
            {
                if(n>capacity())
                    reserve(n);
                while(_finish!=_start+n)//如果_finish不在末尾,初始化后面的空间为val值
                {
                    *_finish=val;
                    ++_finish;
                }
            }
        }

resize扩容也需要传入一个扩容大小的值n,但是resize扩容可以自定义扩容后的空间中的值:val,这个val值可以缺省,默认为匿名对象。它的扩容和reserve相同,只不过支持空间初始化和删除多余n个的数据。

当需要扩容的容量n小于原本的大小,那么就删除原本数据,直到留下n个数据;扩容的容量大于原本空间时,则初始化后面所有的值为val值。

Tip:匿名对象:

		T();//此时生命周期只有这一行,因为没人会用它了 const T&
		xx=T();//当它赋值给const类型引用时会延长生命周期,生命周期跟随xx一起 ```

当匿名对象被const类型引用时就会延长生命周期了,延长到引用域结束。

vector的插入和删除

push_back尾插:

				void push_back(const T& x)
        {
            if(_finish==_end_of_storage)
            {
//        this->reserve(capacity()== 0 ? 4 : capacity()*2);
                reserve(capacity()== 0 ? 4 : capacity()*2);
            }
            *_finish=x;
            ++_finish;
        }

尾插很简单,传入一个值x,再判断空间是否需要扩容,如果需要则扩容,然后在尾地址插入数据,后移尾地址_finish即可。

pop尾删

void pop()//尾删
        {
            assert(!empty());
            --_finish;
        }
bool empty()//判断是否为空
        {
            return _start==_finish;
        }

需要断言是否为空,空的数组无法再删除,把_finish前移一位即可删除尾部数据,因为_finish中的数据被屏蔽,即完成尾删

insert插入

iterator insert(iterator pos,const T& val)
        {
            assert(pos>=_start);
            assert(pos<=_finish);//可以在_finish处插入
            if(_finish==_end_of_storage)//检查容量避免while越界移动数组
            {
                size_t len=pos-_start;//记录pos和_start的相对距离
								//如果扩容,扩容后的_start地址改变,迭代器pos地址还是以前的地址,迭代器失效
                reserve(capacity()== 0 ? 4 : capacity()*2);
                pos=_start+len;//更新pos位置 解决pos迭代器失效   tips:pos还是一个指针类型的形参,这里改变,外面不改变且失效
            }
            iterator end=_finish-1;//_finish的位置是null值
            while(end>=pos)
            {
                *(end+1)=*end;//移动数组
                --end;
            }
            *pos=val;//pos位置插入val
            ++_finish;
						return pos;
        }

pos位置插入val值,首先断言pos位置,不能超出这个vector数组。首先检查_finish位置,是否达到了空间最大值,如果_finish等于_end_of_storage,此时移动数组会越界访问,则需要扩容。

扩容时会遇到一个迭代器失效的问题(下面会说到),传入函数的pos值是未扩容前数组的地址位置,扩容之后,原先的_start已经被释放,所以pos是个野指针,要更新pos的位置,从而解决pos迭代器失效。

首先在扩容前记录原先pos和_start的相对距离存入len中,再执行扩容操作,最后更新pos位置,更新后的pos位置即是_start加上相对位置len。

空间问题解决完后,即可把pos后的数据往后移动,最后在pos位置插入val值,不要忘记++_finish移动尾地址。

最后返回pos以便外部继续使用这个pos位置的数据。

erase删除

iterator erase(iterator pos)
        {
            assert(pos<_finish);//_finish处没有元素不能删除
            assert(pos>=_start);
            iterator start=pos+1;
            while(start!=_finish)//数据前移
            {
                *(start-1)=*start;
                ++start;
            }
            --_finish;//删除最后一个数据
						return pos;//更新pos,返回pos值以便外部继续使用
        }

指定pos位置数据删除,和insert一样先断言pos位置是否超出,不一样的一点是,这个pos值不能在_finish位置,因为_finish位置数据被屏蔽认为是空,不能删除。然后把pos后的位置前移到pos位置即可删除pos位的数据,不要忘记- -_finish,屏蔽最后一个值(即是删除)。

最后返回pos值方便外部继续调用这个pos位置的数据。

例如:

void test3()
    {
        vector<int> v1;
        v1.push_back(1);
        v1.push_back(2);
        v1.push_back(3);
        v1.push_back(4);
        v1.push_back(5);
        auto it = v1.begin();
        while (it != v1.end())
        {
            if (*it % 2 == 0)
            {
                it = v1.erase(it);
            }
            else
            {
                ++it;
            }
        }
        Print(v1);
    }

在循环中,每次erase删除满足条件的值后,更新it的pos值,继续往后查找满足条件的值。

Tip:string中的erase使用迭代器同理。

*迭代器失效

外部迭代器失效问题:

				auto pos=find(v1.begin(),v1.end(),3);//区间找3
        if(pos!=v1.end())
        {
            v1.insert(pos,333);//pos位置插入333
        }
        //严格来说pos用过后失效了,不能再使用
        //(*pos)++;
        Print(v1);
        pos= find(v1.begin(),v1.end(),333);
        v1.erase(pos);
        //erase后也失效,不要访问,行为结果为定义(和编译器有关)

pos是迭代器类型的变量,当我们使用find函数找到区间中的值后,赋值它的所在地址给pos,然后在pos位置insert插入一个值,执行完insert操作后,当内存空间未满,那么pos指向的是新插入数据的地址下标,即是333这个数据的地址,pos原本应是3所在的地址空间,它现在指向333,虽然可以对pos进行操作,但是会误认为这个数据是3,因为上面find的是3,不是333!

还有一种情况:插入数据时内存空间已经满了,insert会进行扩容

扩容操作会导致里面形参pos的指针变量会因为扩容而改变地址,且原来的地址空间被delete释放了,但pos是一个指针类型的形参,外部的pos值不会改变。如果这个插入操作扩容了空间,那么这个pos是野指针,指向的是未知空间,它失效了,不能再使用了。

如果还需要使用pos,我们需要重新给它“找”一个新的数据地址。

当然erase操作后pos也会失效,不要再访问pos,使用pos的行为结果未定义,这个行为结果和编译器有关。

到此vecotr的基本功能和操作都已经实现,附上全部源代码。

#include <assert.h>

using namespace std;

namespace lty{
    template <class T>
    class vector
    {
    public:
        typedef T* iterator;
        typedef const T* const_iterator;

        vector()
        {}

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

        // [first, last) 左必右开区间
        template <class InputIterator>
        vector(InputIterator first, InputIterator last)
        {
            while (first != last)
            {
                push_back(*first);
                ++first;
            }
        }

        //v2(v1)深拷贝
//        vector(const vector<T>& v)
//        {
//            _start = new T[v.capacity()];
//            //memcpy(_start, v._start, sizeof(T)*v.size());//浅拷贝
//            for (size_t i = 0; i < v.size(); ++i)
//            {
//                _start[i] = v._start[i];//当vector<vector<int>>时,是一个二维数组,没写赋值重载,是一个浅拷贝,需要再写赋值重载
//            }
//
//            _finish = _start + v.size();
//            _end_of_storage = _start + v.capacity();
//        }

        vector(const vector<T>& v)
        {
            vector<T> tmp(v.begin(),v.end());//借助迭代器拷贝来把v中的数据全部拷贝进tmp中
            swap(tmp);//tmp和this交换数据即可实现二维v2(v1)深拷贝
        }

        vector<T>& operator=(vector<T> v)
        {
            swap(v);//this和形参v交换,交换后的v因为是形参所以不会改变
            return *this;
        }

        void swap(vector<T> &v)
        {
            std::swap(_start,v._start);
            std::swap(_finish,v._finish);
            std::swap(_end_of_storage,v._end_of_storage);
        }
        ~vector()
        {
            delete[] _start;
            _start=_finish=_end_of_storage= nullptr;
        }

        iterator begin()
        {
            return _start;
        }
        iterator end()
        {
            return _finish;
        }
        const_iterator begin() const
        {
            return _start;
        }
        const_iterator end() const
        {
            return _finish;
        }

        void resize(size_t n,T val=T())
        {
            if(n<size())
            {
                _finish=_start+n;//删除数据
            }
            else
            {
                if(n>capacity())
                    reserve(n);
                while(_finish!=_start+n)//如果_finish不在末尾,初始化后面的空间为val值
                {
                    *_finish=val;
                    ++_finish;
                }
            }
        }
        void reserve(size_t n)
        {
            if(n>capacity())//判断是否缩容
            {
                size_t sz=size();//储存扩容前的size大小
                T* tmp=new T[n];
                if(_start)//如果不为空
                {
//                    memcpy(tmp,_start,sizeof(T)*size());/当vector中的类型是string时是浅拷贝
                    for (size_t i = 0; i < sz; ++i)
                    {
                        tmp[i]=_start[i];
                    }
                    delete[] _start;
                }
                _start=tmp;//此时_start地址已改变,如果直接用size(),会导致_finish位置出错
                _finish=_start + sz;
           // _finish=_start + size();//_finish - _start 抵消剩_finish,且size()为负数
                _end_of_storage=_start+n;
            }
        }
        void push_back(const T& x)
        {
            if(_finish==_end_of_storage)
            {
//        this->reserve(capacity()== 0 ? 4 : capacity()*2);
                reserve(capacity()== 0 ? 4 : capacity()*2);//this调用扩容
            }
            *_finish=x;
            ++_finish;
        }

        void pop()//尾删
        {
            assert(!empty());
            --_finish;
        }

        void insert(iterator pos,const T& val)
        {
            assert(pos>=_start);
            assert(pos<=_finish);//可以在_finish处插入
            if(_finish==_end_of_storage)//检查容量避免while越界移动数组
            {
                size_t len=pos-_start;//记录pos和_start的相对距离
                //如果扩容,扩容后的_start地址改变,迭代器pos地址还是以前的地址,迭代器失效
                reserve(capacity()== 0 ? 4 : capacity()*2);
                pos=_start+len;//更新pos位置 解决pos迭代器失效   tips:pos还是一个指针类型的形参,这里改变,外面不改变且失效
            }
            iterator end=_finish-1;//_finish的位置是null值
            while(end>=pos)
            {
                *(end+1)=*end;//移动数组
                --end;
            }
            *pos=val;//pos位置插入val
            ++_finish;
        }

        iterator erase(iterator pos)
        {
            assert(pos<_finish);//_finish处没有元素不能删除
            assert(pos>=_start);
            iterator start=pos+1;
            while(start!=_finish)//数据前移
            {
                *(start-1)=*start;
                ++start;
            }
            --_finish;//删除最后一个数据
            return pos;//返回pos值以便外部继续使用迭代器,解决迭代器失效问题
        }

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

        T& operator[](size_t pos)
        {
            assert(pos<size());
            return _start[pos];
        }
        const T& operator[](size_t pos) const//函数重载 给const类型用
        {
            assert(pos<size());
            return _start[pos];
        }

        bool empty()//判断是否为空
        {
            return _start==_finish;
        }
    private:
        iterator _start = nullptr;
        iterator _finish = nullptr;
        iterator _end_of_storage = nullptr;


    };



    void Print(const vector<int> v)
    {
//        for(size_t i=0;i<v.size();++i)
//        {
//            cout << v[i] << " ";
//        }
//        cout << endl;
//        auto it=v.begin();
//        while(it<v.end())
//        {
//            cout << *it << " ";
//            ++it;
//        }
//        cout << endl;
        for(auto a:v)
        {
            cout << a << " ";
        }
        cout << endl;
    }

    //测试
    void test1()//push_back尾插+resize
    {
        vector<int> v1;
        v1.push_back(1);
        v1.push_back(2);
        v1.push_back(3);
        v1.push_back(4);
        v1.push_back(5);
        Print(v1);
        v1.resize(10);
        Print(v1);
    }
    void test2()
    {
        vector<int> v1;
        v1.push_back(1);
        v1.push_back(2);
        v1.push_back(3);
        v1.push_back(4);
        v1.push_back(5);
        auto pos=find(v1.begin(),v1.end(),3);//区间找3
        if(pos!=v1.end())
        {
            v1.insert(pos,333);//pos位置插入333
        }
        //严格来说pos用过后失效了,不能再使用
        //(*pos)++;
        Print(v1);
        pos= find(v1.begin(),v1.end(),333);
        v1.erase(pos);
        //erase后也失效,不要访问,行为结果未定义(和编译器有关)
        Print(v1);
    }
    void test3()//迭代器更新,解决迭代器pos失效问题
    {
        vector<int> v1;
        v1.push_back(1);
        v1.push_back(2);
        v1.push_back(3);
        v1.push_back(4);
        v1.push_back(5);
        auto it = v1.begin();
        while (it != v1.end())
        {
            if (*it % 2 == 0)
            {
                it = v1.erase(it);
            }
            else
            {
                ++it;
            }
        }
        Print(v1);
    }
    void test4()
    {

    }
}






  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值