文章目录
- 1. 介绍
- 2. vector类的使用
- 
- 2.1 vector类对象的构造函数
- 2.2 vector类对象的容量操作
- 2.3 vector类对象的访问及遍历操作
- 2.4 vector类对象的修改操作
 
- 3. vector类的模拟实现
1. 介绍
vector是表示可变大小数组的序列容器。
就像数组一样,vector也采用的连续存储空间来存储元素。也就是意味着可以采用下标对vector的元素进行访问,和数组一样高效。但是又不像数组,它的大小是可以动态改变的,而且它的大小会被容器自动处理。
本质讲,vector使用动态分配数组来存储它的元素。当新元素插入时候,这个数组需要被重新分配大小为了增加存储空间。其做法是,分配一个新的数组,然后将全部元素移到这个数组。就时间而言,这是一个相对代价高的任务,因为每当一个新的元素加入到容器的时候,vector并不会每次都重新分配大小。
vector分配空间策略:vector会分配一些额外的空间以适应可能的增长,因为存储空间比实际需要的存储空间更大。不同的库采用不同的策略权衡空间的使用和重新分配。但是无论如何,重新分配都应该是对数增长的间隔大小,以至于在末尾插入一个元素的时候是在常数时间的复杂度完成的。
因此,vector占用了更多的存储空间,为了获得管理存储空间的能力,并且以一种有效的方式动态增长。
与其它动态序列容器相比(deque, list and forward_list), vector在访问元素的时候更加高效,在末尾添加和删除元素相对高效。对于其它不在末尾的删除和插入操作,效率更低。比起list和forward_list统一的迭代器和引用更好。
2. vector类的使用
2.1 vector类对象的构造函数
| (constructor)构造函数代码 | 功能说明 | 
|---|---|
| (默认构造函数)构造一个空的容器,没有任何元素。 | |
| (填充构造函数)构造一个包含 n 个元素的容器,每个元素都是 val 的副本。 | |
| (拷贝构造函数)构造一个容器,其中的每个元素都是 x 中对应元素的副本,顺序相同。 | |
| (范围构造函数)根据范围 | 
2.2 vector类对象的容量操作
| 函数名称 | 代码 | 功能说明 | 
|---|---|---|
| size | 返回向量中元素的个数,即向量的大小。 | |
| max_size | 返回向量可以容纳的最大元素数量。 | |
| resize | 改变容器的大小,使其包含 n 个元素。如果 n 小于当前容器的大小,内容将被缩减为其前 n 个元素,并移除超出范围的元素。如果 n 大于当前容器的大小,内容将扩展,通过在末尾插入足够数量的元素,使容器的大小达到 n。如果提供了 val 参数,则新元素将被初始化为 val 的副本,否则它们将进行值初始化。如果 n 也大于当前容器的容量,将进行自动重新分配已分配的存储空间。 | |
| capacity | 返回当前为向量分配的存储空间的大小,以元素为单位。这个容量不一定等于向量的大小。它可以相等或更大,额外的空间允许在不需要在每次插入时重新分配的情况下进行增长。这个容量并不限制向量的大小。当容量耗尽并需要更多空间时,容器会自动扩展(重新分配存储空间)。 | |
| empty | 返回向量是否为空(即其大小是否为 0)。 | |
| reserve | 请求向量的容量至少为 n 个元素。如果 n 大于当前向量的容量,该函数会导致容器重新分配存储空间,将容量增加到 n(或更大)。在所有其他情况下,函数调用不会导致重新分配,并且向量的容量不受影响。 | 
2.3 vector类对象的访问及遍历操作
| 函数名称 | 代码 | 功能说明 | 
|---|---|---|
| operator[] | 返回向量容器中位置 n 处的元素的引用。 | |
| at | 返回向量中位置 n 处的元素的引用。该函数会自动检查 n 是否在向量的有效元素范围内,如果不在范围内(即 n 大于或等于向量的大小),则抛出 out_of_range 异常。这与成员运算符 operator[] 不同,后者不进行边界检查。 | |
| front | 返回向量中第一个元素的引用。 | |
| back | 返回向量中最后一个元素的引用。 | 
遍历操作
#include <iostream>
#include <vector>
int main()
{
	std::vector<int> v(10,100);
	// 1.普通下标遍历
	for (size_t i = 0; i < v.size(); ++i)
		std::cout << v[i] << " ";
	std::cout << '
';
	// 2.迭代器遍历
	for (std::vector<int>::iterator it = v.begin(); it != v.end(); ++it)
		std::cout << *it << " ";
	std::cout << '
';
	// 3.范围for遍历
	for (auto e : v)
		std::cout << e << " ";
	std::cout << '
';
	return 0;
}
输出结果

说明
- 普通下标遍历:
在此代码段中,通过使用普通的下标操作符[] ,从索引 0 开始,逐个访问向量v 中的元素,并将其打印出来。循环变量i 从 0 递增到v.size()-1 ,并使用v[i] 访问每个元素。最后,打印一个换行符。- 迭代器遍历:
在此代码段中,使用迭代器进行遍历。首先,通过v.begin() 获取向量v 的起始迭代器,通过v.end() 获取向量v 的结束迭代器。然后,通过迭代器it 遍历从起始迭代器到结束迭代器之间的所有元素,并使用*it 打印每个元素的值。最后,打印一个换行符。- 范围for循环遍历:
在此代码段中,使用范围for循环对向量v 进行遍历。对于向量v 中的每个元素,将其依次赋值给循环变量e ,然后打印出e 的值。此方法不需要显式地使用迭代器或下标来访问向量的元素,因为范围for循环会自动处理迭代过程,并在每次迭代中将元素赋值给循环变量。最后,打印一个换行符。
2.4 vector类对象的修改操作
| 函数名称 | 代码 | 功能说明 | 
|---|---|---|
| assign | 将新的内容赋值给向量,用新内容替换向量当前的内容,并相应地调整向量的大小。 | |
| push_back | 在向量的末尾添加一个新元素 val。 | |
| pop_back | 删除最后一个元素。 | |
| insert | 在指定位置 position 之前插入新元素 val(n 个 val)。 | |
| erase | 从向量中删除单个元素(位置)或一段元素范围( | |
| clear | 清空向量中的所有元素,使向量变为空。 | 
3. vector类的模拟实现
// vector.h文件
#pragma once
#include <iostream>
#include <assert.h>
using namespace std;
namespace my_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;
        }
        //construct and destroy
        vector()
        {}
        vector(int n, const T& value = T())
        {
            resize(n, value);
        }
        template<class InputIterator>
        vector(InputIterator first, InputIterator last)
        {
            while (first != last)
            {
                push_back(*first);
                first++;
            }
        }
        vector(const vector<T>& v)
        {
            reserve(v.capacity());
            for (auto& e : v)
            {
                push_back(e);
            }
        }
        vector<T>& operator=(vector<T> v)
        {
            swap(v);
            return *this;
        }
        ~vector()
        {
            delete[] _start;
            _start = _finish = _endOfStorage = nullptr;
        }
        // capacity
        size_t size() const
        {
            return _finish - _start;
        }
        size_t capacity() const
        {
            return _endOfStorage - _start;
        }
        void reserve(size_t n)
        {
            if (n > capacity())
            {
                iterator tmp = new T[n];
                size_t sz = size();
                if (_start)
                {
                    for (size_t i = 0; i < sz; i++)
                    {
                        tmp[i] = _start[i];
                    }
                }
                delete[] _start;
                _start = tmp;
                _finish = tmp + sz;
                _endOfStorage = tmp + n;
            }
        }
        void resize(size_t n, const T& value = T())
        {
            if (n <= size())
            {
                _finish = _start + n;
            }
            else
            {
                reserve(n);
                while (_finish < _start + n)
                {
                    *_finish = value;
                    _finish++;
                }
            }
        }
        ///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(_start != _finish);
            _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 >= _start);
            assert(pos < _finish);
            if (_finish == _endOfStorage)
            {
                size_t len = pos - _start;
                reserve(capacity() == 0 ? 4 : 2 * capacity());
                pos = _start + len;
            }
            iterator end = _finish;
            while (pos < end)
            {
                *end = *(end - 1);
                end--;
            }
            *pos = x;
            ++_finish;
            return pos;
        }
        iterator erase(iterator pos)
        {
            assert(pos >= _start);
            assert(pos < _finish);
            iterator it = pos;
            while (it < _finish - 1)
            {
                *it = *(it + 1);
                it++;
            }
            _finish--;
            return pos;
        }
    private:
        iterator _start = nullptr; // 指向数据块的开始
        iterator _finish = nullptr; // 指向有效数据的尾
        iterator _endOfStorage = nullptr; // 指向存储容量的尾
    };
}