第一章:vector的介绍及使用
1.1 vector的介绍
- vector是表示可变大小数组的序列容器。
- 就像数组一样,vector也采用的连续存储空间来存储元素。也就是意味着可以采用下标对vector的元素进行访问,和数组一样高效。但是又不像数组,它的大小是可以动态改变的,而且它的大小会被容器自动处理。
- 本质讲,vector使用动态分配数组来存储它的元素。当新元素插入时候,这个数组需要被重新分配大小为了增加存储空间。其做法是,分配一个新的数组,然后将全部元素移到这个数组。就时间而言,这是一个相对代价高的任务,因为每当一个新的元素加入到容器的时候,vector并不会每次都重新分配大小。
- vector分配空间策略:vector会分配一些额外的空间以适应可能的增长,因为存储空间比实际需要的存储空间更大。不同的库采用不同的策略权衡空间的使用和重新分配。但是无论如何,重新分配都应该是对数增长的间隔大小,以至于在末尾插入一个元素的时候是在常数时间的复杂度完成的。
- 因此,vector占用了更多的存储空间,为了获得管理存储空间的能力,并且以一种有效的方式动态增长。
- 与其它动态序列容器相比(deque, list and forward_list), vector在访问元素的时候更加高效,在末尾添加和删除元素相对高效。对于其它不在末尾的删除和插入操作,效率更低。比起list和forward_list统一的迭代器和引用更好。
使用STL的三个境界:能用,明理,能扩展 ,那么下面学习vector,我们也是按照这个方法去学习
1.2 vector的使用
vector学习时一定要学会查看文档:vector在实际中非常的重要,在实际中我们熟悉常见的接口就可以,下面列出了哪些接口是要重点掌握的。
1.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); |
使用迭代器进行初始化构造 |
1.2.2 vector iterator 的使用
iterator的使用 |
接口说明 |
begin + end(重点) |
获取第一个数据位置的iterator/const_iterator, 获取最后一个数据的下一个位置的iterator/const_iterator |
rbegin + rend |
获取最后一个数据位置的reverse_iterator,获取第一个数据前一个位置的reverse_iterator |


1.2.3 vector 空间增长问题
容量空间 |
接口说明 |
size |
获取数据个数 |
capacity |
获取容量大小 |
empty |
判断是否为空 |
resize(重点) |
改变vector的size |
reserve (重点) |
改变vector的capacity |
1.2.4 vector 增删查改
vector增删查改 |
接口说明 |
push_back(重点) |
尾插 |
pop_back (重点) |
尾删 |
find 查找 |
查找。(注意这个是算法模块实现,不是vector的成员接口) |
insert |
在position之前插入val |
erase |
删除position位置的数据 |
swap |
交换两个vector的数据空间 |
operator[] (重点) |
像数组一样访问 |
vector的使用代码
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <vector>
using namespace std;
void test_vector1() {
vector<int> v1;//无参构造
vector<int> v2(10, 1);//构造并初始化10个1
vector<int> v3(v2.begin(), v2.end());//迭代器区间初始化
//可以用其他容器迭代器初始化,因为这里构造函数是函数模版,但数据类型要匹配
string str("hello world");
vector<int> v4(str.begin(), str.end());//这里数据类型严格来说不匹配,但可以走隐式类型转换
vector<int> v5(v4);//拷贝构造
//遍历vector
for (size_t i = 0; i < v3.size(); i++)
cout << v3[i] << " ";
cout << endl;
//vector<int>::iterator it = v4.begin();
auto it = v4.begin();
while (it != v4.end()) {
cout << *it << " ";
++it;
}
cout << endl;
for (auto e : v5)
cout << e << " ";
cout << endl;
}
void test_vector2() {
// 测试vector的默认扩容机制
size_t sz;
vector<int> v;
//v.reserve(100);//如果reserve100个空间就不会再扩容
//v.resize(100);//还会继续扩容,因为size也到100了,下方代码插入的数据是从101位置开始
sz = v.capacity();
cout << "making v grow:\n";
for (int i = 0; i < 100; ++i) {
v.push_back(i);
if (sz != v.capacity()) {
sz = v.capacity();
cout << "capacity changed: " << sz << '\n';
}
}
//vs:运行结果:vs下使用的STL基本是按照1.5倍方式扩容
//making foo grow :
//capacity changed : 1
//capacity changed : 2
//capacity changed : 3
//capacity changed : 4
//capacity changed : 6
//capacity changed : 9
//capacity changed : 13
//capacity changed : 19
//capacity changed : 28
//capacity changed : 42
//capacity changed : 63
//capacity changed : 94
//capacity changed : 141
//g++运行结果:linux下使用的STL基本是按照2倍方式扩容
//making foo grow :
//capacity changed : 1
//capacity changed : 2
//capacity changed : 4
//capacity changed : 8
//capacity changed : 16
//capacity changed : 32
//capacity changed : 64
//capacity changed : 128
}
void test_vector3() {
vector<int> v1;
cout << v1.max_size() << endl;//max_size没有实际意义
vector<int> v;
//v.reserve(100);//该方式开空间,capacity虽然是100,但size=0,不能使用下面的方式插入数据。
v.resize(100);//size = 100 capacity = 100
for (size_t i = 0; i < v.size(); i++) //size是0,所以没有进循环。
v[i] = i; //将v.size()改为100也不可以,[]有断言,只能访问0~size-1
for (auto e : v)
cout << e << " ";
cout << endl;
}
void test_vector4() {
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
for (auto e : v)
cout << e << " "; // 1 2 3 4
cout << endl;
v.insert(v.begin(), 0);//头插0
for (auto e : v)
cout << e << " "; // 0 1 2 3 4
cout << endl;
//在3前面插入30
//该find是算法库提供的,而不是vector提供。因为很多数据容器查找逻辑是相同的。
//string自带find是因为除了找字符,还可能要找字符串
auto it = find(v.begin(), v.end(), 3);//通过迭代器找3
if (it != v.end())
v.insert(it, 30);
for (auto e : v)
cout << e << " ";// 0 1 2 30 3 4
cout << endl;
//删除3
it = find(v.begin(), v.end(), 3);
if (it != v.end())
v.erase(it);
for (auto e : v)
cout << e << " ";// 0 1 2 30 4
cout << endl;
//clear只清除数据
cout << v.size() << endl;//5
cout << v.capacity() << endl;//6
v.clear();
cout << v.size() << endl;//0
cout << v.capacity() << endl;//6
v.shrink_to_fit();//clear后再调用shrink就释放空间了
}
int main() {
//test_vector1();
//test_vector2();
//test_vector3();
test_vector4();
return 0;
}
第二章:vector深度剖析及模拟实现
2.1 vector的模拟实现
Vector.h
#pragma once
#include <assert.h>
#include <string>
namespace bit {
template <class T>
class vector {
public:
typedef T* iterator;//typedef受到访问限定符限制,如果不在公有,后续无法使用迭代器的begin()和end()等
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; }
//vector()
// :_start(nullptr), _finish(nullptr), _end_of_storage(nullptr) {}
//优化版本 - 在成员变量声明处给缺省值
vector() {} //这个构造函数虽然没内容,但必须保留,因为有了迭代器区间构造和拷贝构造编译器就不再生成默认构造函数
//类模版里面还可以继续写模版函数。
//下方示例中如果将InputIterator模版替换为iterator,那么vector迭代器区间初始化就只能用vector类型的迭代器
//但实际上,有使用其他类型迭代器初始化的需求
template <class InputIterator>
vector(InputIterator first, InputIterator last) { //这里已经在成员变量声明处给缺省值
while (first != last) {
push_back(*first);
++first;
}
}
vector(size_t n, const T& val = T()) { //n个val初始化
reserve(n);
for (size_t i = 0; i < n; i++)
push_back(val);
}
//预期是vector<int> v1(10, 0)调用上方n个val初始化的构造函数,但该构造函数第一个参数是size_t,第二个参数是int。
//而vector(InputIterator first, InputIterator last)迭代器区间初始化构造函数两个参数是同类型,更匹配。
//为了解决vector<int> v1(10, 0)会匹配到vector(InputIterator first, InputIterator last),重载一个int版本
vector(int n, const T& val = T()) { //n个val初始化
reserve(n);
for (int i = 0; i < n; i++)
push_back(val);
}