【C++】vector


describe

  1. A vector is a sequence container representing a variable-sized array.
  2. Just like arrays, vectors also use contiguous storage space to store elements. That means that the elements of the vector can be accessed using subscripts, which is as efficient as an array. But unlike an array, its size can be changed dynamically, and its size will be automatically handled by the container.
  3. Essentially, vector uses a dynamically allocated array to store its elements. When new elements are inserted, the array needs to be resized in order to increase storage space. It does this by allocating a new array and then moving all elements into this array. In terms of time, this is a relatively expensive task, because the vector does not resize each time a new element is added to the container.
  4. vector allocation space strategy: vector will allocate some additional space to accommodate possible growth, because the storage space is larger than the actual storage space required. Different libraries employ different strategies for weighing space usage and reallocation. But in any case, the reallocation should be of logarithmically growing interval size, so that inserting an element at the end is done in constant time complexity.
  5. Therefore, vector takes up more storage space, in order to gain the ability to manage storage space, and grow dynamically in an efficient way.
  6. Compared with other dynamic sequence containers (deque, list and forward_list), vector is more efficient when accessing elements, and adding and removing elements at the end is relatively efficient. For other deletion and insertion operations that are not at the end, it is less efficient. Better than list and forward_list unified iterators and references.

One, vector (standard library)

common interface

insert image description here

vector uses

traverse
insert image description here

find element, insert element
insert image description here

Constructor
insert image description here

  When we wrote Yang Hui's triangle before, we used a two-dimensional array. After learning vector, we can also use vector to solve the problem of Yang Hui's triangle.

leetcode Yang Hui triangle problem

class Solution {
    
    
public:
    vector<vector<int>> generate(int numRows) {
    
    
    		//这里的vector<vector<int>>我们就可把他看做一个二维数组,由于内部实现一些函数,
    		//所以可以使用[]直接像数组那样使用,但是不是数组,是内部实现的运算符重载
            vector<vector<int>> vv;
            vv.resize(numRows,vector<int>());

            for(size_t i=0;i<vv.size();i++)
            {
    
    
                vv[i].resize(i+1,0); //扩容
                vv[i][0]=vv[i][vv[i].size()-1]=1; //将最外层初始化为1
            }

            for(size_t i=0;i<vv.size();i++)
            {
    
    
                for(size_t j=0;j<vv[i].size();j++)
                {
    
    
                    if(vv[i][j]==0)
                    {
    
    
                        vv[i][j]=vv[i-1][j]+vv[i-1][j-1];
                    }
                }
            }
            return vv;
    }
};

Two, vector simulation implementation

1. Default member functions

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);
			}
		}
		//重载,为防止构造器匹配到别的函数模板,需重载一个int类型的
		vector(int n, const T& val = T())
		{
    
    
			reserve(n);
			for (int i = 0; i < n; ++i)
			{
    
    
				push_back(val);
			}
		}

		//支持任意类型的迭代器构造,我们传迭代器可以传入不同类型,需要写泛型函数模板
		template<class InputIterator>
		vector(InputIterator first, InputIterator last)
		{
    
    
			while (first != last)
			{
    
    
				push_back(*first);
				first++;
			}
		}

		//拷贝构造
		vector(const vector<T>& v)
		{
    
    
			//提前开好所用空间,可以提高效率
			reserve(v.capacity());
			_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];
			}
			//更新
			_finish = _start + v.size();
			_end_of_storage = _start + v.capacity();
		}
		//析构函数
		~vector()
		{
    
    
			//通过new出来的空间需要使用delete手动使用,再将指针指向空,防止内存泄露
			delete[] _start;
			_start = _finish = _end_of_storage = nullptr;
		}
	private: 
		//C++11 内置类型可以传缺省值,这里的缺省值传给初始化列表初始化
		iterator _start=nullptr;  //指向数组开头的指针
		iterator _finish=nullptr;	//指向数组最后一个有效元素的指针
		iterator _end_of_storage=nullptr;	//指向数组最后一个位置的指针
	};

2.resize

	//开空间 + 初始化
	void resize(size_t n,const T val=T())  //使用匿名函数初始化,这里为了可以初始化,可以使用 int a =int();
	{
    
    
		if (n < size())
		{
    
    
			//n<size,说明要调整的大小比我们原来的要小,删除数据,其实就是将指针位置改变一下,不需要真的删除元素
			_finish = _start + n;
		}
		else
		{
    
    
			
			if (n > capacity())
			{
    
    
				//这里 n>capacity 这种情况说明,要调整的大小比我们最大容量还要大,此时我们需要扩容
				reserve(n);
			}
			while (_finish != _start + n)
			{
    
    
				*_finish = val;
				++_finish;
			}
		}
	}

3.reserve

	void reserve(size_t n)
	{
    
    
		if (n > capacity())
		{
    
    
			size_t sz = size();
			//申请空间
			T* tmp = new T[n];
			//如果原数组中有元素,拷贝到新申请的空间上	
			if (_start)
			{
    
    
				for (size_t i = 0; i < sz; i++)
				{
    
    
					tmp[i] = _start[i];
				}
				delete[] _start;
			}
			//更新
			_start = tmp;
			_finish = _start + sz;  //小坑,不可使用size(),因为更新完_start后size()发生改变
			_end_of_storage = _start + n;
		}
	}

4. Tail insertion, tail deletion

	//尾插
	void push_back(const T& x)
	{
    
    
		if (_finish == _end_of_storage)
		{
    
    
			//扩容
			reserve(capacity()==0?4:capacity()* 2);
		}
		*_finish = x;
		++_finish;
	}
	//尾删
	void pop_back()
	{
    
    
		assert(!empty()); //断言vector是否为空
		--_finish;
	}

5.insert (insert)

	void insert(iterator pos, const T& val)
	{
    
    
		assert(pos >= _start);
		assert(pos <= _finish);
	
		if (_finish == _end_of_storage)
		{
    
    
			//迭代器失效问题,需要更新pos,因为我们使用reserve是异地扩容,迭代器的值已被改变,我们需要记录一下相对位置
			size_t len = pos - _start;
			reserve(capacity() == 0 ? 4 : capacity() * 2);
	
			//扩容后更新pos,解决pos失效问题
			pos = _start + len;
		}
	
		iterator end = _finish - 1;
		while (end >= pos)
		{
    
    
			*(end + 1) = *end;
			--end;
		}
		*pos = val;
		++_finish;
	}

6.erase (delete)

iterator erase(iterator pos)
	{
    
    
		//也有迭代器失效问题,野指针  所以我们在删除指定位置的元素后,不可再使用原来的迭代器进行操作,返回一个新的迭代器可解决此问题
		assert(pos >= _start);
		assert(pos < _finish);

		iterator start = pos + 1;
		//挪动数据
		while (start != _finish)
		{
    
    
			*(start - 1) = *start;
			++start;
		}
		--_finish;

		return pos;
	}

7. [] operator overloading

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

8.empty (judgment empty), capacity (capacity size), size (number of elements)

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

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

Guess you like

Origin blog.csdn.net/Tianzhenchuan/article/details/131470403