文章目录
- 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; // 指向存储容量的尾 }; }