C++ STL vector的模拟实现

1. vector的介绍和使用

  • vector是表示可变大小数组的序列容器。
  • 就像数组一样,vector也采用的连续存储空间来存储元素。也就是意味着可以采用下标对vector的元素进行访问,和数组一样高效。但是又不像数组,它的大小是可以动态改变的,而且它的大小会被容器自动处理。
  • 本质讲,vector使用动态分配数组来存储它的元素。当新元素插入时候,这个数组需要被重新分配大小为了增加存储空间。其做法是,分配一个新的数组,然后将全部元素移到这个数组。就时间而言,这是一个相对代价高的任务,因为每当一个新的元素加入到容器的时候,vector并不会每次都重新分配大小。
  • vector分配空间策略:vector会分配一些额外的空间以适应可能的增长,因为存储空间比实际需要的存储空间更大。不同的库采用不同的策略权衡空间的使用和重新分配。但是无论如何,重新分配都应该是对数增长的间隔大小,以至于在末尾插入一个元素的时候是在常数时间的复杂度完成的。
  • 因此,vector占用了更多的存储空间,为了获得管理存储空间的能力,并且以一种有效的方式动态增长。
  • 与其它动态序列容器相比(deques, lists and forward_lists), vector在访问元素的时候更加高效,在末尾添加和删除元素相对高效。对于其它不在末尾的删除和插入操作,效率更低。比起lists和forward_lists统一的迭代器和引用更好。

更为详细的可以查看vector文档介绍

2. vector的模拟实现

vector的嵌套型别定义

typedef _Ty         value_type;
typedef value_type* iterator;
typedef value_type& reference;
typedef size_t      size_type;

vector的成员变量

private:
        iterator _start;
        iterator _last;
        iterator _end;

2.1 vector构造函数和拷贝构造函数

vector():_start(nullptr),_last(nullptr),_end(nullptr)
{}
vector(size_type n,const _Ty& value):_start(nullptr),_last(nullptr),_end(nullptr)
{
     insert(n,value);
}
vector(iterator f,iterator l):_start(nullptr),_last(nullptr),_end(nullptr)
{
     insert(f,l);
}
vector(const vector<int>& iv)
{
        reserve(iv.capacity());
        iterator it = begin();
        iterator vit = iv.end();
        while (vit != iv.begin())
        {
              *it++ = *vit--;
        }
}

2.2 insert函数和eraser函数

iterator insert(iterator pos,const _Ty& value)
{
    //1.当size()==capacity()时,表明vector已满,再进行插入前需要进行扩容
    if(size()== capacity())
    {
        size_type oldpos = pos - begin();
        //这里需要防止一种情况:若vector为空的时候,他的capacity为0,这个时候给他直接扩容2倍是行不通的,
        //因为2*0 = 0,因此就需要进行判断
        size_type newcapacity = (capacity() == 0)? 1 : 2*capacity();

        reserve(newcapacity);

        //这里空间发生了变化,pos迭代器会失效,因此需要重新对pos进行设置
        //reserve不会使vector的成员变量失效
        pos = begin() + oldpos;
    }
    //2.当size() < capacity()时,表明vector未满,插入直接在pos的位置进行插入
    //需要注意的是插入是在pos指向的位置进行插入,并且插入需要挪动数据,
    //将pos位置之后的数据全部向后挪动一个,为防止元素被改写,则需要从后向前进行挪动
    iterator tail = _last;
    while(tail > pos)
    {
        *tail = *(tail-1);
        --tail;
    }
    //这里要注意的是挪动数据时,因为没有对pos位置进行操作,所以pos位置的迭代器并没有失效,
    //但是pos位置之后的迭代器全部失效了,但在这里并没有关系,我们并不会用到那些迭代器
    *pos = value;

    //插入完之后,一定要对_last指针+1,因为全部向后挪动了一个元素
    ++_last;

    return pos;
}

void insert(size_type n,const _Ty& value)
{
    for(int i = 0;i < n; ++i)
    {
        insert(end(),value);
    }
}
void insert(iterator f,iterator l)
{
    while(f!=l)
    {
        insert(end(),*f);
        ++f;
    }
}

iterator erase(iterator pos)
{
    assert(pos >= _start || pos < _last);
    //1.删除pos位置的元素,就是将[pos,end()]这个区间向前挪动一个即可
    iterator it = pos + 1;
    while(it != _last)
    {
        *(it-1) = *(it);
        ++it;
    }

    --_last;
    return pos;
}

2.3 reserve函数和resize函数

void reserve(size_type n)
{
    //若 n 的值大于vector的容量,则开辟空间
    //若 n 的值小于等于,则不进行任何操作
    if(n > capacity())
    {
        //1.新开辟一个空间
        size_type oldSize = size();
        _Ty* newVector = new _Ty[n];
        //2.将原空间的数值赋值到新空间
        if(_start)
        {
            //注意:这里不能使用memcpy,因为memcpy是一个浅拷贝。
            //memcpy(newVector,_start,sizeof(_Ty)*size());
            for(size_type i = 0; i < oldSize; ++i)
            {
                newVector[i] = _start[i];
            }
        }
        //3.改变三个指针的指向
        //这里直接重新给三个成员进行赋值,所以调用reserve()函数不用担心迭代器失效的问题
        _start = newVector;
        _last = _start + oldSize;
        _end = _start + n;
    }
}

void resize(size_type n,const _Ty& value = _Ty())
{
    //1.如果n的值小于等于size()的时候,则只需要将_last的指针往前移动即可
    if(n <= size())
    {
        _last = _start + n;
        return;
    }
    //2.如果n的值大于capacity()的时候,则需调用reserve()函数,重新设置容量大小
    if(n > capacity())
    {
        reserve(n);
    }
    //若当n的值大于size()而小于capacity()的时候,只需将_last的指针往后移即可

    iterator it = _last;
    _last = _start + n;

    while(it != _last)
    {
        *it = value;
        ++it;
    }
    //resize()函数也不需要担心迭代器失效的问题
}

2.4 push_back函数和pop_back函数

void push_back(const _Ty& value)
{
    insert(end(),value);
}
void pop_back()
{
    erase(end()-1);
}

2.5 begin函数和end函数

iterator begin()const
{
    return _start;
}
iterator end() const
{
    return _last;
}

2.6 size函数、capacity函数

size_type size()
{
    return end()-begin();
}
size_type capacity()const
{
    return _end-begin();
}

2.7 empty函数和operator[]重载

bool empty()const
{
    return end() == begin();
}

reference operator[](size_type n)
{
    return *(begin() + n);
}

2.8 完整代码和相应测试

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

using namespace std;

namespace mytest{
    template<class _Ty>
    class vector
    {
        public:
            typedef _Ty         value_type;
            typedef value_type* iterator;
            typedef value_type& reference;
            typedef size_t      size_type;
        public:
            iterator begin()const
            {
                return _start;
            }
            iterator end() const
            {
                return _last;
            }
            size_type size()
            {
                return end()-begin();
            }
            size_type capacity()const
            {
                return _end-begin();
            }
            bool empty()const
            {
                return end() == begin();
            }
            reference operator[](size_type n)
            {
               return *(begin() + n);
            }

        public:
            vector():_start(nullptr),_last(nullptr),_end(nullptr)
            {}
            vector(size_type n,const _Ty& value):_start(nullptr),_last(nullptr),_end(nullptr)
            {
                insert(n,value);
            }
            vector(iterator f,iterator l):_start(nullptr),_last(nullptr),_end(nullptr)
            {
                insert(f,l);
            }
            vector(const vector<int>& iv)
            {
                reserve(iv.capacity());
                iterator it = begin();
                iterator vit = iv.end();
                while (vit != iv.begin())
                {
                    *it++ = *vit--;
                }
            }
        public:
            void reserve(size_type n)
            {
                //若 n 的值大于vector的容量,则开辟空间
                //若 n 的值小于等于,则不进行任何操作
                if(n > capacity())
                {
                    //1.新开辟一个空间
                    size_type oldSize = size();
                    _Ty* newVector = new _Ty[n];
                    //2.将原空间的数值赋值到新空间
                    if(_start)
                    {
                        //注意:这里不能使用memcpy,因为memcpy是一个浅拷贝。
                        //memcpy(newVector,_start,sizeof(_Ty)*size());
                        for(size_type i = 0; i < oldSize; ++i)
                        {
                            newVector[i] = _start[i];
                        }
                    }
                    //3.改变三个指针的指向
                    //这里直接重新给三个成员进行赋值,所以调用reserve()函数不用担心迭代器失效的问题
                    _start = newVector;
                    _last = _start + oldSize;
                    _end = _start + n;
                }
            }

            void resize(size_type n,const _Ty& value = _Ty())
            {
                //1.如果n的值小于等于size()的时候,则只需要将_last的指针往前移动即可
                if(n <= size())
                {
                    _last = _start + n;
                    return;
                }
                //2.如果n的值大于capacity()的时候,则需调用reserve()函数,重新设置容量大小
                if(n > capacity())
                {
                    reserve(n);
                }
                //若当n的值大于size()而小于capacity()的时候,只需将_last的指针往后移即可

                iterator it = _last;
                _last = _start + n;

                while(it != _last)
                {
                    *it = value;
                    ++it;
                }
                //resize()函数也不需要担心迭代器失效的问题
            }

            void push_back(const _Ty& value)
            {
                insert(end(),value);
            }
            void pop_back()
            {
                erase(end()-1);
            }

            iterator insert(iterator pos,const _Ty& value)
            {
                //1.当size()==capacity()时,表明vector已满,再进行插入前需要进行扩容
                if(size()== capacity())
                {
                    size_type oldpos = pos - begin();
                    //这里需要防止一种情况:若vector为空的时候,他的capacity为0,
                    //这个时候给他直接扩容2倍是行不通的,因为2*0 = 0,因此就需要进行判断
                    size_type newcapacity = (capacity() == 0)? 1 : 2*capacity();

                    reserve(newcapacity);

                    //这里空间发生了变化,pos迭代器会失效,因此需要重新对pos进行设置
                    //reserve不会使vector的成员变量失效
                    pos = begin() + oldpos;
                }
                //2.当size() < capacity()时,表明vector未满,插入直接在pos的位置进行插入
                //需要注意的是插入是在pos指向的位置进行插入,并且插入需要挪动数据,
                //将pos位置之后的数据全部向后挪动一个,为防止元素被改写,则需要从后向前进行挪动
                iterator tail = _last;
                while(tail > pos)
                {
                    *tail = *(tail-1);
                    --tail;
                }
                //这里要注意的是挪动数据时,因为没有对pos位置进行操作,所以pos位置的迭代器并没有失效,
                //但是pos位置之后的迭代器全部失效了,但在这里并没有关系,我们并不会用到那些迭代器
               *pos = value;

               //插入完之后,一定要对_last指针+1,因为全部向后挪动了一个元素
               ++_last;

               return pos;
            }

            void insert(size_type n,const _Ty& value)
            {
                for(int i = 0;i < n; ++i)
                {
                    insert(end(),value);
                }
            }
            void insert(iterator f,iterator l)
            {
                while(f!=l)
                {
                    insert(end(),*f);
                    ++f;
                }
            }

            iterator erase(iterator pos)
            {
                assert(pos >= _start || pos < _last);
                //1.删除pos位置的元素,就是将[pos,end()]这个区间向前挪动一个即可
                iterator it = pos + 1;
                while(it != _last)
                {
                    *(it-1) = *(it);
                    ++it;
                }

                --_last;
                return pos;

            }

        private:
            iterator _start;
            iterator _last;
            iterator _end;
    };

};

void Test1()
{
    mytest::vector<int> iv;

    cout << "iv.size() = " << iv.size() << endl;
    cout << "iv.capacity() = " << iv.capacity() << endl;
    iv.push_back(1);
    iv.push_back(2);
    iv.push_back(3);
    iv.push_back(4);
    cout << "iv.size() = " << iv.size() << endl;
    cout << "iv.capacity() = " << iv.capacity() << endl;

    mytest::vector<int>::iterator it = iv.begin();

    while(it != iv.end())
    {
        cout << *it << " ";
        ++it;
    }
    cout << endl;
    iv.pop_back();
    iv.pop_back();
    it = iv.begin();
    while(it != iv.end())
    {
        cout << *it << " ";
        ++it;
    }
    cout << endl;

}

void Test2()
{
    mytest::vector<int> iv(10,2);
    mytest::vector<int>::iterator it = iv.begin();
    while(it != iv.end())
    {
        cout << *it << " ";
        ++it;
    }
    cout << endl;
}

void Test3()
{
    int ar[] = {1,2,3,3,4,5};
    mytest::vector<int> iv(ar,ar+6);
    mytest::vector<int>::iterator it = iv.begin();
    while(it != iv.end())
    {
        cout << *it << " ";
        ++it;
    }
    cout << endl;

}
int main()
{
//    Test1();
//    Test2();
    Test3();
    return 0;
}

到此这篇关于C++ STL vector的模拟实现的文章就介绍到这了,更多相关C++ STL vector内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 详解C++ STL vector容量(capacity)和大小(size)的区别

    很多初学者分不清楚 vector 容器的容量(capacity)和大小(size)之间的区别,甚至有人认为它们表达的是一个意思.本节将对 vector 容量和大小各自的含义做一个详细的介绍. vector 容器的容量(用 capacity 表示),指的是在不分配更多内存的情况下,容器可以保存的最多元素个数:而 vector 容器的大小(用 size 表示),指的是它实际所包含的元素个数. 对于一个 vector 对象来说,通过该模板类提供的 capacity() 成员函数,可以获得当前容器的容量

  • C++(STL库)之顺序容器vector的使用

    一.特点 ①总的来说:可变大小数组.支持快速随机访问.在尾部之外的位置插入或删除元素可能很慢 ②元素保存在连续的内存空间中,因此通过下标取值非常快 ③在容器中间位置添加或删除元素非常耗时 ④一旦内从重分配,和原vector相关的指针,引用,迭代器都失效.内存重分配耗时很长 二.头文件.using声明 头文件:#include <vector> using声明:using std::vector; 三.初始化 vector<T>  v1; ==>v1是一个空的vector ve

  • 详解C++ STL vector容器访问元素的几种方式

    学会如何创建并初始化 vector 容器之后,本节继续来学习如何获取(甚至修改)容器中存储的元素. 访问vector容器中单个元素 首先,vector 容器可以向普通数组那样访问存储的元素,甚至对指定下标处的元素进行修改,比如: #include <iostream> #include <vector> using namespace std; int main() { vector<int> values{1,2,3,4,5}; //获取容器中首个元素 cout &l

  • C++ STL入门教程(1) vector向量容器使用方法

    一.简介 Vectors 包含着一系列连续存储的元素,其行为和数组类似. 访问Vector中的任意元素或从末尾添加元素都可以在O(1)内完成,而查找特定值的元素所处的位置或是在Vector中插入元素则是O(N). 二.完整程序代码 /*请务必运行以下程序后对照阅读*/ #include <vector> #include <iostream> #include <algorithm> #include <stdexcept> using namespace

  • C++ STL vector的模拟实现

    1. vector的介绍和使用 vector是表示可变大小数组的序列容器. 就像数组一样,vector也采用的连续存储空间来存储元素.也就是意味着可以采用下标对vector的元素进行访问,和数组一样高效.但是又不像数组,它的大小是可以动态改变的,而且它的大小会被容器自动处理. 本质讲,vector使用动态分配数组来存储它的元素.当新元素插入时候,这个数组需要被重新分配大小为了增加存储空间.其做法是,分配一个新的数组,然后将全部元素移到这个数组.就时间而言,这是一个相对代价高的任务,因为每当一个新

  • 深入浅析STL vector用法

    本文关于stl vector用法的介绍非常详细,具体内容请看下文 介绍 这篇文章的目的是为了介绍std::vector,如何恰当地使用它们的成员函数等操作.本文中还讨论了条件函数和函数指针在迭代算法中使用,如在remove_if()和for_each()中的使用.通过阅读这篇文章读者应该能够有效地使用vector容器,而且应该不会再去使用C类型的动态数组了. Vector总览 vector是C++标准模板库中的部分内容,它是一个多功能的,能够操作多种数据结构和算法的模板类和函数库.vector之

  • C++中vector的模拟实现实例详解

    目录 vector接口总览 默认成员函数 构造函数 拷贝构造 赋值重载 析构函数 迭代器相关函数 begin和end 容量相关函数 size和capacity reserve resize empty 修改容器相关函数 push_back pop_back insert erase swap 访问容器相关函数 operator[ ] 总结 vector接口总览 namespace nzb { //模拟实现vector template<class T> class vector { publi

  • 详解C++ STL模拟实现vector

    目录 vector 概述 接口总览 默认成员函数 构造函数 析构函数 拷贝构造函数 复制赋值函数 vector 的迭代器 元素访问 operator[] back 容量相关函数 size capacity empty resize reserve 修改函数 insert push_back erase pop_back swap vector 概述 vector 的数据结构安排及操作方式,与原生数组十分相似,两者唯一的差别在于空间运用的灵活性.原生数组是静态空间,一旦配置了就不能改变大小:vec

  • C++ vector类的模拟实现方法

    vector和string虽然底层都是通过顺序表来实现的,但是他们利用顺序表的方式不同,string是指定好了类型,通过使用顺序表来存储并对数据进行操作,而vector是利用了C++中的泛型模板,可以存储任何类型的数据,并且在vector中,并没有什么有效字符和容量大小的说法,底层都是通过迭代器进行操作的,迭代器底层实现也就是指针,所以说,vector是利用指针对任何顺序表进行操作的. vector属性 _start用于指向第一个有效元素 _finish用于指向最后一个有效元素的下一个位置 _e

  • STL中vector的使用你了解吗

    目录 前言 1.vector是什么 2.vector创建对象 3.遍历vector (1)下标遍历 (2)迭代器遍历 (3)范围for遍历 3.vector容量函数 4.vector的扩容函数 5.vector的插入删除 (1)尾插与尾删 (2)insert与erase (3)clear vector的特殊应用 6.总结 前言 当我们使用C语言的语法来实现数组的时候,增删查改都需要自己来定义函数,STL中给出了数组模板vector,其中包含函数可以帮助我们更便捷地处理数组. 1.vector是什

  • 利用C++模拟实现STL容器:list

    目录 一.list的介绍 二.list的排序 三.迭代器 1.list的迭代器失效问题 2.迭代器的功能分类 3.list迭代器的模拟实现 4.迭代器价值 5.迭代器operator->的重载 四.模拟实现时遇到的困惑及注意点 五.vector和list的优缺点 1.vector 2.list 六.模拟实现list整体代码 一.list的介绍 列表是一种顺序容器,它允许在序列中的任何位置执行常量时间插入和删除操作,并允许在两个方向上进行迭代. 它的底层是一个带头双向循环链表.附一篇博主用C语言写

  • c++ vector模拟实现的全过程

    一.vector是什么? vector是表示可变大小数组的序列容器,它也采用连续存储空间来存储元素,因此可以采用下标对vector的元素进行访问,它的大小是动态改变的,vector使用动态分配数组来存储它的元素: 二.容器特性 1.顺序序列 顺序容器中的元素按照严格的线性顺序排序.可以通过元素在序列中的位置访问对应的元素; 2.动态数组 支持对序列中的任意元素进行快速直接访问,甚至可以通过指针进行该操作.操供了在序列末尾相对快速地添加/删除元素的操作; 3.能够感知内存分配器的 容器使用一个内存

随机推荐