vector

目录

七、vector

(一)vector的介绍及使用

1、介绍 

2、使用

1)定义

​编辑 2)迭代器使用

3)vector 空间增长问题  

注意

4)vector增删查改

3、迭代器失效

 (二)vector的模拟实现

1、浅拷贝问题

 2、代码


七、vector

(一)vector的介绍及使用

1、介绍 

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

2、使用

1)定义

 vector是序列容器,表示可以改变大小的数组,也就是顺序表。

(constructor) 构造函数声明
接口说明
vector(); 无参构造
vector(size_type n,const value_type& val = value_type()); 构造并初始化n个val
vector(const vector& x); 拷贝构造
vector (InputIterator first, InputIterator last);
使用迭代器进行初始化构造

vector才是类型,T是一模版

迭代器传参也可以传string的迭代器

int main()
{
	vector v1();
	vector v2(10,3);
	vector v3(v2);
	vector v4(v2.begin(),v2.end());
	string s = "hello";
	vector v5(s.begin(),s.end());

	cout << "v2 :";
	for (int i = 0; i < v2.size(); i++)
	{
		cout << v2[i] << " ";
	}
	cout << endl;
	cout << "————————————————" << endl;
	cout << "v3 :";
	vector ::iterator it = v3.begin();
	while (it != v3.end())
	{
		cout << *it << " ";
		it++;
	}
	cout << endl;
	cout << "————————————————" << endl;
	cout << "v4 :";
	for (auto e : v4)
	{
		cout << e << " ";
	}
	cout << endl;
	cout << "————————————————" << endl;
	cout << "v5 :";
	for (auto e : v5)
	{
		cout << e << " ";
	}
	cout << endl;
	cout << "————————————————" << endl;
	return 0;
}

vector_第1张图片

当然,这只是些常见的构造,还有一些其他的,这里就不再举例

vector_第2张图片 2)迭代器使用

 和string迭代器一样

vector_第3张图片

之前上边的代码也使用了迭代器进行遍历

cout << "v3 :";
vector ::iterator it = v3.begin();
while (it != v3.end())
{
	cout << *it << " ";
	it++;
}
cout << endl;
cout << "————————————————" << endl;
cout << "v4 :";
for (auto e : v4)
{
	cout << e << " ";
}
cout << endl;
cout << "————————————————" << endl;

3)vector 空间增长问题  
函数名称 作用
size 获取数据个数
capacity 获取容量大小
empty 判断是否为空
resize 改变vector的size
reserve 改变vector的capacity
注意
1、capacity 的代码在 vs g++ 下分别运行会发现, vs capacity 是按 1.5 倍增长的, g++ 是按 2 倍增长的
2、这个问题经常会考察,不要固化的认为, vector 增容都是 2 倍,具体增长多少是根据具体的需求定义的。vs PJ 版本 STL g++ SGI 版本 STL 。reserve只负责开辟空间,如果确定知道需要用多少空间, reserve 可以缓解 vector 增容的代价缺陷问题。
3、resize 在开空间的同时还会进行初始化,影响 size

void func2()
{
	vector v = { 1,2,3,4,5,6 };
	cout << "size: " << v.size() << endl;
	cout << "capacity: " << v.capacity() << endl;
	cout << "empty: " << v.empty() << endl;
	cout << "————————————————" << endl;
	v.resize(10);
	cout << "size: " << v.size() << endl;
	cout << "capacity: " << v.capacity() << endl;
	cout << "————————————————" << endl;
	v.reserve(12);
	cout << "size: " << v.size() << endl;
	cout << "capacity: " << v.capacity() << endl;
}

vector_第4张图片

4)vector增删查改
函数名称 作用
push_back 尾插
pop_back 尾删
find(不在vector中) 查找
insert
position 之前插入 val
erase
删除 position 位置的数据
swap 交换
operator[] 访问

在学数据结构的时候,我们自己写顺序表有头插头删,但是vector没有直接提供它们,因为效率太低,不希望我们使用,但是我们可以用insert和erase来实现头插头删,但是能不用就不用,效率太低了。

void func3()
{
	vector v = { 1,2,3 };
	auto e = find(v.begin(), v.end(), 2);
	cout << *e << endl;
	cout << "————————————————" << endl;
	for (auto e : v)
	{
		cout << e << " ";
	}
	cout << endl;
	cout << "————————————————" << endl;
	v.push_back(4);
	v.push_back(5);
	for (auto e : v)
	{
		cout << e << " ";
	}
	cout << endl;
	cout << "————————————————" << endl;
	v.pop_back();
	for (int i = 0; i < v.size(); i++)
	{
		cout << v[i] << " ";
	}
	cout << endl;
	cout << "————————————————" << endl;
	v.insert(v.begin() + 1, 600);
	v.insert(v.begin() + 3, 600);
	for (auto e : v)
	{
		cout << e << " ";
	}
	cout << endl;
	cout << "————————————————" << endl;
	v.erase(v.begin() + 1);
	v.erase(v.begin() + 2);
	for (auto e : v)
	{
		cout << e << " ";
	}
	cout << endl;
	cout << "————————————————" << endl;
	vector tmp = { 100,200,300 };
	cout << "v: ";
	for (auto e : v)
	{
		cout << e << " ";
	}
	cout << endl<<"tmp: ";
	for (auto e : tmp)
	{
		cout << e << " ";
	}
	cout << endl;
	cout << "————————————————" << endl;
	swap(v, tmp);
	cout << "v: ";
	for (auto e : v)
	{
		cout << e << " ";
	}
	cout << endl<<"tmp: ";
	for (auto e : tmp)
	{
		cout << e << " ";
	}
	cout << endl;
	cout << "————————————————" << endl;
}

vector_第5张图片

 值得注意的是reserve,当我们只扩容,然后去访问新扩容的位置时,你会发现↓

void func4()
{
	vector v = { 1,2,3 };
	cout << "size: " << v.size() << endl;
	cout << "capacity: " << v.capacity() << endl;
	cout << "————————————————" << endl;
	v.reserve(10);
	cout << "size: " << v.size() << endl;
	cout << "capacity: " << v.capacity() << endl;
	cout << "————————————————" << endl;
	cout << v[8] << endl;
}

vector_第6张图片

vector_第7张图片

因为vector中的[]重载只允许0~size-1范围内,超出该范围会有断言

如果非要这么访问,我们就要用resize

void func4()
{
	vector v = { 1,2,3 };
	cout << "size: " << v.size() << endl;
	cout << "capacity: " << v.capacity() << endl;
	cout << "————————————————" << endl;
    //v.reserve(10);    //size = 3 , capacity = 10;
	v.resize(10);       //size = 10 , capacity = 10;
	cout << "size: " << v.size() << endl;
	cout << "capacity: " << v.capacity() << endl;
	cout << "————————————————" << endl;
	cout << v[8] << endl;
}

 find是不在vector中写入的,而是在算法库中,在任何地方都可以使用,它是个模版

vector_第8张图片

 比如我需要在“2”的前面加个“100”,但是我不知道2是在哪个位置

	vector v = { 1,2,3 };
	for (auto e : v)
	{
		cout << e << " ";
	}
	cout << endl;
	auto i = find(v.begin(), v.end(), 2);
	v.insert(i, 100);
	for (auto e : v)
	{
		cout << e << " ";
	}
	cout << endl;

函数名称 作用
clear 清空
shrink_to_fit 将空间缩短至size

 注意,clear只是清空数据,空间大小不变

void func5()
{
	vector v = { 1,2,3,4,5 };
	cout << "size: " << v.size() << endl;
	cout << "capacity: " << v.capacity() << endl;
	cout << "————————————————" << endl;
	v.clear();
	cout << "size: " << v.size() << endl;
	cout << "capacity: " << v.capacity() << endl;
	cout << "————————————————" << endl;
	v.shrink_to_fit();
	cout << "size: " << v.size() << endl;
	cout << "capacity: " << v.capacity() << endl;
	cout << "————————————————" << endl;
}

vector_第9张图片

以上就是vector常用的函数,当然vector还有很多,这里不再举例

vector_第10张图片

有兴趣的,可以去官网查看 icon-default.png?t=N7T8https://legacy.cplusplus.com/

3、迭代器失效

 失效一:

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

当我们模拟实现vector的insert函数时,我们这样写代码会发现,当需要扩容时,会产生随机数

vector_第11张图片

这就是因为迭代器失效了

当扩容时是异地扩容,地址会改变,但是pos的值却没变,所以我们需要更新pos值

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

 失效二:

 vector::const_iterator it = v.begin();
 v.insert(v.begin() + 1, 10);
 while (it != v.end())
 {
     cout << *it << " ";
     it++;
 }
 cout << endl;

vector_第12张图片 也是失效问题,虽然我们改变了pos值,但这是传值参数

形参只是实参的一份临时拷贝,改变形参直接不会形象实参

vector_第13张图片

 失效三:

        iterator erase(iterator pos)
		{
			assert(pos >= _start);
			assert(pos < _finish);
			iterator it = pos + 1;
			while (it < _finish)
			{
				*(it - 1) = *it;
				++it;
			}
			--_finish;
			return pos;
		}
vector v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
v.push_back(5);
v.push_back(6);
v.push_back(7);


vector::iterator it = v.begin();
while (it != v.end())
{
    cout << *it << " ";
    it++;
}
cout << endl;
it = v.begin();
while (it != v.end())
{
    if (*it % 2 == 0)
    {
        v.erase(it);
    }
    it++;
}
 it = v.begin();
while (it != v.end())
{
    cout << *it << " ";
    it++;
}

这个代码是可以正常运行的

 但是当最后一个元素是偶数时

 vector v;
 v.push_back(1);
 v.push_back(2);
 v.push_back(3);
 v.push_back(4);
 v.push_back(5);
 v.push_back(6);
 v.push_back(7);
 v.push_back(8);

 vector::iterator it = v.begin();
 while (it != v.end())
 {
     cout << *it << " ";
     it++;
 }
 cout << endl;
 it = v.begin();
 while (it != v.end())
 {
     if (*it % 2 == 0)
     {
         v.erase(it);
     }
     it++;
 }
  it = v.begin();
 while (it != v.end())
 {
     cout << *it << " ";
     it++;
 }

会崩掉

以及当偶数在一起时

vector v;
v.push_back(1);
v.push_back(2);
v.push_back(2);
v.push_back(4);
v.push_back(5);
v.push_back(6);
v.push_back(7);

vector::iterator it = v.begin();
while (it != v.end())
{
    cout << *it << " ";
    it++;
}
cout << endl;
it = v.begin();
while (it != v.end())
{
    if (*it % 2 == 0)
    {
        v.erase(it);
    }
    it++;
}
 it = v.begin();
while (it != v.end())
{
    cout << *it << " ";
    it++;
}

 结果不对

所以我们需要利用erase的返回值对it进行更新

vector v;
v.push_back(1);
v.push_back(2);
v.push_back(2);
v.push_back(4);
v.push_back(5);
v.push_back(6);
v.push_back(7);
v.push_back(8);

vector::iterator it = v.begin();
while (it != v.end())
{
    cout << *it << " ";
    it++;
}
cout << endl;
it = v.begin();
while (it != v.end())
{
    if (*it % 2 == 0)
    {
        it = v.erase(it);/
    }
    else
         it++;/
}
 it = v.begin();
while (it != v.end())
{
    cout << *it << " ";
    it++;
}

所以,insert和erase都会涉及迭代器失效问题,要注意! 

 (二)vector的模拟实现

1、浅拷贝问题

当vector的每一个元素为string时

  vector()
      :_start(nullptr)
      ,_finish(nullptr)
      ,_endOfStorage(nullptr)
  {}
  ~vector()
  {
      delete[] _start;
      _start = _finish = _endOfStorage = nullptr;
  }
void reserve(size_t n)
{
    if(n > capacity())
    {
        T* tmp = new T[n];
        int sz = size();
        if (_start != nullptr)
        {
            memcpy(tmp, _start, sizeof(T) * sz);
            delete[] _start;
        }
        _start = tmp;
        _finish = _start + sz;
        _endOfStorage = _start + n;
    }
}
void push_back(const T& x)
 {
     if (_finish == _endOfStorage)
     {
         reserve(capacity() == 0 ? 4 : 2 * capacity());
     }
     *_finish = x;
     _finish++;
 }
        vector v;
        v.push_back("111111111111111111");
        v.push_back("111111111111111111");
        v.push_back("111111111111111111");
        v.push_back("111111111111111111");

        vector::iterator it = v.begin();
        while (it != v.end())
        {
            cout << *it << " ";
            it++;
        }
    }

没有什么问题 ,但当插入第五个元素,需要扩容时

        vector v;
        v.push_back("111111111111111111");
        v.push_back("111111111111111111");
        v.push_back("111111111111111111");
        v.push_back("111111111111111111");
        v.push_back("111111111111111111");

        vector::iterator it = v.begin();
        while (it != v.end())
        {
            cout << *it << " ";
            it++;
        }
    }

发生了问题,这是为什么呢? 

刚开完空间是这样的

vector_第14张图片

 memcpy后是这样的

vector_第15张图片

delete后是把_start释放掉,但是它的里面是自定义类型,就会调用自定义类型的析构函数,把_str释放掉,所以原来的信息也会变为随机值

也就是浅拷贝问题,memcpy是浅拷贝,我们需要的是深拷贝,所以memcpy不能在这里使用

   void reserve(size_t n)
   {
       if(n > capacity())
       {
           T* tmp = new T[n];
           int sz = size();
           if (_start != nullptr)
           {
               //memcpy(tmp, _start, sizeof(T) * sz);
               for (size_t i = 0; i < sz; i++)
               {
                   tmp[i] = _start[i];
               }
               delete[] _start;
           }
           _start = tmp;
           _finish = _start + sz;
           _endOfStorage = _start + n;
       }
   }

vector_第16张图片

 2、代码

代码仅供参考

#include
#include
using namespace std;
namespace YM
{
    template
    class vector
    {
    public:
        // Vector的迭代器是一个原生指针
       typedef T* iterator;
       typedef const T* const_iterator;
        iterator begin()
        {
            return _start;
        }
        iterator end()
        {
            return _finish;
        }
        const_iterator cbegin() const
        {
            return _finish;
        }
        const_iterator cend() const
        {
            return _start;
        }
            // construct and destroy
        vector()
            :_start(nullptr)
            ,_finish(nullptr)
            ,_endOfStorage(nullptr)
        {}
        vector(int n, const T& value = T())
            : _start(nullptr)
            , _finish(nullptr)
            , _endOfStorage(nullptr)
        {
            reserve(n);
            for (size_t i = 0; i < n; i++)
            {
                push_back(value);
            }
        }
        template
        vector(InputIterator first, InputIterator last)
        {
            while (first != last)
            {
                push_back(*first);
                first++;
            }
        }
        vector(const vector& v)
            : _start(nullptr)
            , _finish(nullptr)
            , _endOfStorage(nullptr)
        {
            reserve(v.capacity());
            iterator it = begin();
            const_iterator vit = v.cbegin();
            while (vit != v.cend())
            {
                *it++ = *vit++;
            }
            _finish = it;
        }
        vector& operator= (vector v)
        {
            swap(v);
            return *this;
        }
        
        ~vector()
        {
            delete[] _start;
            _start = _finish = _endOfStorage = nullptr;
        }
        
        void reserve(size_t n)
        {
            if(n > capacity())
            {
                T* tmp = new T[n];
                int sz = size();
                if (_start != nullptr)
                {
                    //memcpy(tmp, _start, sizeof(T) * sz);
                    for (size_t i = 0; i < sz; i++)
                    {
                        tmp[i] = _start[i];
                    }
                    delete[] _start;
                }
                _start = tmp;
                _finish = _start + sz;
                _endOfStorage = _start + n;
            }
        }
        void resize(size_t n, const T& value = T())
        {
            if (n > capacity())
            {
                reserve(n);
                while (_finish < _start + n)
                {
                    *_finish = value;
                    _finish++;
                }
            }
            else
            {
                _finish = _start + n;
            }
        }
            ///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++;
        }
        void pop_back()
        {
            assert(_finish > _start);
            if (_start + 1 == _finish)
            {
                delete[] _start;
                _start = _finish = _endOfStorage = nullptr;
            }
            else
            {
                _finish--;
            }
        }
        void swap(vector& 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 <= _finish);
            assert(pos >= _start);
            if (_finish == _endOfStorage)
            {
                size_t len = pos - _start;
                reserve(capacity() == 0 ? 4 : 2 * capacity());
                pos = _start + len;
            }
            iterator a = _finish - 1;
            while (a >= pos)
            {
                *(a+1) = *a;
                a--;
            }
            _finish++;
            *pos = x;
            return pos;
        }
        iterator erase(iterator pos)
        {
            assert(pos < _finish);
            assert(pos >= _start);
            iterator a = pos+1;
            while (a < _finish)
            {
                *(a-1) = *a;
                a++;
            }
            _finish--;
            return pos;
        }
        size_t size() const
        {
            return _finish - _start;
        }
        size_t capacity() const
        {
            return _endOfStorage - _start;
        }
    private:
        iterator _start; // 指向数据块的开始
        iterator _finish; // 指向有效数据的尾
        iterator _endOfStorage; // 指向存储容量的尾
    };
}

你可能感兴趣的:(C++,c++)