vector
文章目录
0.是什么
template < class T, class Alloc = allocator<T> > class vector; // generic template
------>是一个类模板
头文件:<vector>
1.构造
cpp
vector<int>v1;//创建一个空vector
vector<int>v2(10, 1);//创建一个vector,里面有10个1
vector<int>v3(++v2.begin(), --v2.end());//按迭代器创建一个vector,里面有8个1
//迭代器换成数组下标
int arr[] = {1, 2, 3, 4, 5};
vector<int> v4(arr, arr + 5);
vector<int> v { 2,3,4,6,1,7 };//后面再说
vector<int>类模板实例化vector<int> v1;用这个 类 定义了一个对象(变量)v1,并调用默认构造函数。vector<int> v2(10, 1);用这个 类 定义了一个对象(变量)v1,并调用 构造函数
关于类模板的知识在这里
为什么直接用数组下标也能构造?
指针满足迭代器的所有要求,指针可以直接用来充当迭代器
vector有(范围)构造函数:
template <class InputIterator>
vector(InputIterator first, InputIterator last);是一个模板函数,它不关心 传进来的 类型,只要 满足 " 迭代器" 就行。
------>通过这个模版生成函数来构造
(模板函数自动类型推导的知识在这里:【纯干货】C++ 模板核心知识点 函数模板 / 类模板语法 + 面试高频坑(类型推)导 / 实例化 / 分离编译)
2.遍历
- 下标(实际上是 重载operator )
- 通过迭代器
- 范围for
cpp
vector<int> v { 2,3,4,6,1,7 };
for (int i = 0; i < v.size(); i++)
cout << v[i] << ",";
vector<int>::iterator it = v.begin();
while (it != v.end())
{
cout << *it << endl;
++it;
}
for (auto e : v)
cout << e << " ";
有关类和对象的知识,在这里:
【万字干货】C++类和对象从入门到精通:内存划分/访问限定符/this指针/默认成员函数/const用法/类型转换全拆解,附C++实现顺序表、链表、栈------吃透大厂面试高频考点-CSDN博客
AI问了好久!终于搞懂 C++ 命名空间 / 类 / 对象,90% 初学者都踩过的 getline 天坑全解-CSDN博客
类 = 类型,对象 = 用这个类型创建出来的变量。
概念 对应现实事物 说明 头文件( #include <string>)电脑上的文件夹 按功能分类存放代码的容器 命名空间( std)文件夹里的大箱子 防止名字冲突的隔离层 类( std::string)箱子里的小盒子 自定义的类型,描述一类事物的共同属性和行为 对象( std::string s;)用小盒子装的具体物品 用类创建出来的变量,是实际存在的实体 函数( std::getline)箱子里的独立工具 完成特定功能的代码块 全局对象( std::cin)箱子里已经装好的现成工具 标准库提前创建好、可以直接使用的对象
- C++ 标准库所有的东西都在
std这个命名空间里- 同名的命名空间会自动合并
- 不同的标准库头文件,只是把
std这个大命名空间分成了不同的小块
3.reserve
文档中说不会缩容
If n is greater than the current vector capacity, the function causes the container to reallocate its storage increasing its capacity to n (or greater).
In all other cases, the function call does not cause a reallocation and the vector capacity is not affected.
cpp
vector<int> v { 2,3,4,6,1,7 };
cout << "size:" << v.size() << endl << "capacity:" << v.capacity() << endl;
v.reserve(10);
cout << "size:" << v.size() << endl << "capacity:" << v.capacity() << endl;
v.reserve(6);
cout << "size:" << v.size() << endl << "capacity:" << v.capacity() << endl;
size:6
capacity:6
size:6
capacity:10
size:6
capacity:10
4.resize
- n<size,删除数据
- n>size,插入数据(插入传的第二个参数,没传就用
value_type()),空间(capacity)不够就扩容
value_type是容器的元素类型别名 ,value_type()就是该元素类型的默认构造函数。比如:
vector<int>的value_type()就是int()→ 0vector<string>的value_type()就是string()→ 空字符串resize 扩容时,如果你没指定填充值,就用这个默认值填充新元素。
cpp
vector<int> v { 2,3,4,6,1,7 };
for (auto e : v)
cout << e << " ";
cout << endl;
cout << "size:" << v.size() << endl << "capacity:" << v.capacity() << endl;
v.resize(10);
for (auto e : v)
cout << e << " ";
cout << endl;
cout << "size:" << v.size() << endl << "capacity:" << v.capacity() << endl;
v.resize(2);
for (auto e : v)
cout << e << " ";
cout << endl;
cout << "size:" << v.size() << endl << "capacity:" << v.capacity() << endl;
2 3 4 6 1 7
size:6
capacity:6
2 3 4 6 1 7 0 0 0 0
size:10
capacity:10
2 3
size:2
capacity:10
5.插入
5.1push_back
cpp
vector<int> v { 2,3,4,6,1,7 };
v.push_back(88);
5.2insert
‼️注意点:
insert 后迭代器可能失效 (
insert可能导致扩容,所有迭代器、引用、指针失效。)

- 头插/中间插入(通过迭代器)
- 插入多个数据
- 通过迭代器
- 连续插入相同数据
cpp
vector<int> v1 { 2,3,4,6,1,7 };
v1.insert(v1.begin() + 3, 5);
//打印
vector<int> v2 { 1,1,1,1,1,1 };
v1.insert(v1.begin(), v2.begin(), v2.begin() + 4);
//打印
v1.insert(v1.begin(), 2, 0);
//打印
2 3 4 5 6 1 7
1 1 1 1 2 3 4 5 6 1 7
0 0 1 1 1 1 2 3 4 5 6 1 7
手搓
private:
cpp
// vector.h
namespace lcj
{
template<class T>
class vector
{
public:
// ...
private:
iterator _start = nullptr; // 指向数组的起始位置
iterator _finish = nullptr; // 指向最后一个有效数据的下一个位置
iterator _end_of_storage = nullptr; // 指向整个已分配空间的末尾
};
}
size,capacity,empty
cpp
// 返回有效元素个数
size_t size() const { return _finish - _start; }
// 返回总容量
size_t capacity() const { return _end_of_storage - _start; }
// 判断是否为空
bool empty() { return _start == _finish; }
实现迭代器和operator
cpp
// vector的迭代器本质就是原生指针!
typedef T* iterator;
typedef const T* const_iterator;
// 普通迭代器(可以修改元素)
iterator begin() { return _start; }
iterator end() { return _finish; }
// const迭代器(只能读不能改)
const_iterator begin() const { return _start; }
const_iterator end() const { return _finish; }
// 普通版本,可以修改元素
T& operator[](size_t i)
{
assert(i < size()); // 下标越界直接崩溃,比原生数组安全
return _start[i];
}
// const版本,只能读不能改
const T& operator[](size_t i) const
{
assert(i < size());
return _start[i];
}
reserve扩容
此处使用的
memcpy是逐字节拷贝,对于std::string、vector等类类型,会导致浅拷贝,后面会细说
❌错误示例:
-
size()是个函数,这时候会计算:
_finish - _start;,可是这时候:_start = tmp;,start已经不是原来的start了------>计算的size()是错的
cpp
void reserve(size_t n)
{
if (n > capacity())
{
T* tmp = new T[n];
memcpy(tmp, _start, sizeof(T) * size());
delete[] _start;
_start = tmp;
_finish = _start + size();
_end_of_storage = _start + n;
}
}
修改方式1:
- 先改_finish:
_finish = tmp + size();,这时候size()还是用的原来的_finish
cpp
void reserve(size_t n)
{
if (n > capacity())
{
T* tmp = new T[n];
memcpy(tmp, _start, sizeof(T) * size());
delete[] _start;
_finish = tmp + size();
_start = tmp;
_end_of_storage = _start + n;
}
}
修改方式2:
- 提前记录下原来的有效元素个数
cpp
// vector.h
void reserve(size_t n)
{
if (n > capacity())
{
size_t old_size = size(); // 1. 先记录下原来的有效元素个数
T* tmp = new T[n]; // 2. 开新空间
memcpy(tmp, _start, size() * sizeof(T)); // 3. 拷贝数据
delete[] _start; // 4. 释放旧空间
_start = tmp; // 5. 更新三个指针
_finish = tmp + old_size;/////////////////////////////////////////////////////////////////
_end_of_storage = tmp + n;
}
}
push_back
cpp
void push_back(const T& x)
{
// 检查是否需要扩容
if (_finish == _end_of_storage)
{
reserve(capacity() == 0 ? 4 : capacity() * 2);
}
*_finish = x; // 在当前finish位置存放数据
++_finish; // 移动 finish 指针
}