一、基本概念
vector就是顺序表模板。使用时需要包含头文件vector.h
vector不支持流插入和流提取(数据的多样性)。
二、构造函数
(1)无参构造

不传数据,size为0。
(2)有参构造

用n个val构造。
意思就是无参的默认构造的plus版,无参的默认构造其实就是实例化了模板,并没有开空间。这是在无参默认构造的基础上开了n个空间。
(3)拷贝构造

用Bvector对象构造Avector对象。
cpp
int main() {
vector<int> n1;//无参构造(默认为0个数据)
vector<int> n2(6,1);//有参构造(用6个1构造n2)
vector<int> n3(n2);//拷贝构造(用n2构造n1)
cout << n1.size() << endl;//输出0
for (int i = 0; i < n2.size(); i++) {//有参构造
cout << n2[i] << " ";
}
cout << endl;
for (int i = 0; i < n3.size(); i++) {//拷贝构造
cout << n3[i] << " ";
}
cout << endl;
}

(4)迭代器构造

可以用数组、另一个对象的迭代器构造新对象。
cpp
int main() {
int arr[5] = { 1,2,3,4,5 };
vector<int> n1(arr,arr+4);//数组构造
for (int i = 0; i < n1.size(); i++) {
cout << n1[i] << " ";
}
cout << endl;
vector<int> n2(5,0);
vector<int> n3(n2.begin(),n2.end()-1);//n2对象的迭代器
for (int i = 0; i < n3.size(); i++) {
cout << n3[i] << " ";
}
cout << endl;
}

三、遍历
可以用[]、迭代器、范围for遍历vector对象。

cpp
int main() {
vector<const char*> n(5, "love");
for (int i = 0; i < n.size(); i++) {//[]遍历
cout << n[i] << " ";
}
cout << endl;
vector<const char*>:: iterator it = n.begin();//迭代器遍历
while (it != n.end()) {
cout << *it << " ";
it++;
}
cout << endl;
for (auto cur : n) {//范围for遍历
cout << cur << " ";
}
cout << endl;
}

四、reserve
size表示有效元素的个数;capacity表示当前空间的大小;resreve表示开空间,一般编译器不会进行缩容,就算缩容也不会缩到size以下。
cpp
int main() {
vector<int> n(10,1);
cout << n.size() << endl << n.capacity() << endl;
cout << endl;
n.reserve(100);
cout << n.size() << endl << n.capacity() << endl;
}

五、push_back/pop_puck



push_back(尾插);pop_back(尾删);insert(在某个位置插入数据,要使用迭代器)
cpp
int main() {
vector<int> n(1,2);
n.push_back(0);//2后尾插一个0
for (auto n1 : n) {
cout << n1 << " ";
}
cout << endl;
n.pop_back();//删除一个数据(0)
for (auto n1 : n) {
cout << n1 << " ";
}
cout << endl;
}

cpp
int main() {
vector<int> v(6, 0);
v.insert(v.begin() + 2, 3);//在第三个位置插入3
for (auto n : v) {
cout << n << " ";
}
cout << endl;
}

六、erase/clear/swap
(1)erase

删除某个位置的数据;删除从first到last位置的数据(使用迭代器)
cpp
int main() {
int arr[] = { 0,1,2,3,4 };
vector<int> v(arr,arr+5);
v.erase(v.begin());//头删除(删除0)
for (auto n : v) {
cout << n << " ";
}
cout << endl;
v.erase(v.begin(),v.begin()+3);//删除前3个数据
for (auto n : v) {
cout << n << " ";
}
cout << endl;
}

(2)clear
清理对象属性不处理空间。
cpp
int main() {
vector<int> v(100, 0);
cout << v.capacity() << " " << v.size() << endl;
v.clear();
cout << v.capacity() << " " << v.size() << endl;//size变为0,capacity不变
}

(3)swap

交换两个对象的属性,不交换空间。
cpp
int main() {
vector<int> v1(3, 0);
vector<int> v2(3, 1);
v1.swap(v2);//交换后v1变为3个1,v2变为3个0
for (auto n : v1) {
cout << n << " ";
}
cout << endl;
for (auto n : v2) {
cout << n << " ";
}
cout << endl;
}

七、resize

调整元素个数。第二个参数缺省值为0。
resize需要扩容时capacity(n>size)会发生变化;不需要扩容时(n<=size)capacity可能变化也可能不变化(由编译器决定)
cpp
int main() {
vector<int> v(6,2);
v.resize(2);//size调整为2
cout << v.size() <<" "<< v.capacity() << endl;
for (auto n : v) {
cout << n << " ";
}
cout << endl << endl;
v.resize(4,1);//size调整为4,不足的用1补
cout << v.size() << " " << v.capacity() << endl;
for (auto n : v) {
cout << n << " ";
}
}

八、二维数组
我们来看这样一段代码
cpp
int main() {
vector<int> v(3, 1);//实例化第一个模板(一维数组)
vector<vector<int>> vv(3, v);//实例化第二个模板(二维数组)
for (int i = 0; i < vv.size(); i++) {
for (int j = 0; j < v.size(); j++) {
cout << vv[i][j] << " ";
}
cout << endl;
}
cout << endl;
vv[2][1] = 2;//修改元素
for (int i = 0; i < vv.size(); i++) {
for (int j = 0; j < v.size(); j++) {
cout << vv[i][j] << " ";
}
cout << endl;
}
cout << endl;
}

vector<vector<int>>实质就是动态顺序表每一层又套了一个动态顺序表。实例化了两个vector模板,一个是vector<int>,一个是vector<vector<int>>。这里并不等价于于二维数组,只是物理结构上类似。你写成vector<strintg>/vector<int*>也是模拟成了二维数组。如下图:

九、OJ题

思路:构建一个动态的二维数组(每一行的元素个数不同),将每一个元素的值初始化为1。接着从第三行开始遍历每一行的非首非尾元素并修改数据。(第一行和第二行以及每一行的首尾元素不需要修改)
代码实现:

十、vector的模拟实现
(1)注意
在实现string时,我们进行定义和声明的分离实现。因为string是类。在这里,模拟实现vector必须把定义和声明写在同一个头文件里,因为vector是模板类,不支持定义和声明分离的这种写法。
const数据只能传给const引用/指针,非const数据可以传给const。(const可以接受任意的)
迭代器是左闭右开的[begin(),end())。
(2)迭代器失效
①insert的迭代器失效
在实现insert扩容时出现迭代器失效的问题。迭代器失效就是指原有的空间已经释放了,你还在原有的空间上进行操作。(使用了insert之后如果发生扩容就不要访问之前的pos了)
ptrdiff_t:C/C++都能使用。有符号整型,表示两个指针相减的类型,大小和指针相同。
(3)resize的设计
参数为什么这么设计



size_type表示的就是无符号的指针相减类型,通常是size_t

value_type就是T。
value_type()表示T如果实例化的是内置类型,就初始化为0,如果是自定义类型就调用对应的默认构造。
可以形象的理解为内置类型的默认构造()。


(4)模板类里写模板函数
在vector模板类里写迭代器构造就是解决迭代器不是原生指针的问题。
(5)模拟实现
cpp
//模拟实现顺序:命名空间->模板类框架->private->size、capacity、empty->reserve->[]
//push_back->pop_back->insert->resize->析构->拷贝构造->clear->重载=->迭代器构造->n个val构造->拷贝构造函数的深浅拷贝问题
#pragma once
#include<cstring>
#include<iostream>
#include<assert.h>
namespace my_code {
template<class T>
class vector {
public:
typedef T* iterator;
typedef const T* const_iterator;
void reserve(size_t n){//申请空间
if (n > capacity()) {
size_t old_size = size();
iterator tmp = new T[n];
memcpy(tmp,_start,old_size*sizeof(T));
delete[] _start;
_start = tmp;
_finish = tmp + old_size;
_end_of_storage = tmp + n;
}
}
size_t size()const {//返回元素个数(const写了更严谨,承诺我不修改数据)
return _finish - _start;
}
size_t capacity()const {//返回可存储元素最大个数(const写了更严谨,承诺我不修改数据)
return _end_of_storage - _start;
}
bool empty() {//判空
return _start == _end_of_storage;
}
iterator begin() {//普通迭代器
return _start;
}
iterator end() {//普通迭代器
return _finish;
}
const_iterator begin()const {//const迭代器
return _start;
}
const_iterator end()const {//const迭代器
return _finish;
}
void push_back(const T& x) {//尾插
if (_finish == _end_of_storage) {
reserve(size() == 0 ? 4 : 2 * size());
}
*_finish = x;
_finish++;
}
void vector_print() {//打印
iterator it = begin();
while (it != end()) {
std::cout << *it << " ";
it++;
}
std::cout << std::endl;
}
T& operator[](size_t i){//重载[],[]就是用来查找和修改数据的,所以返回值是T&
assert(i < _finish);
return _start[i];
}
const T& operator[](size_t i) const{//重载const[]
assert(i < _finish);
return _start[i];
}
void pop_back() {
assert(!empty());
_finish--;
}
void insert(iterator pos,const T& x) {//在指定迭代器的位置插入数据
//传引用是为了减少拷贝,用const修饰是因为常量的类型为const(const数据只能传const;const可以接受任意的)
assert(pos >=_start && pos < _finish);
if (_finish == _end_of_storage) {//扩容
ptrdiff_t len = pos - _start;//解决迭代器失效的问题
reserve(capacity() == 0 ? 4 : capacity() * 2);
pos = _start + len;//扩容后,原空间已经被释放,pos会发生改变
}
iterator end = _finish - 1;//挪动数据
//*(end + 1) = *end;第一个挪动
//*pos - 1 = *pos;最后一次挪动
while (end >= pos) {
*(end + 1) = *end;
end--;
}
*pos = x;//插入数据
_finish++;
}
void resize(size_t n,T val = T()) {//resize
if (n < size()) {
_finish = _start + n;
}
else {
reserve(n);
while (_finish < _start + n) {
*_finish = val;
++_finish;
}
}
}
~vector() {//析构
if (_start) {
delete[] _start;
_start = _finish = _end_of_storage;
}
}
//vector(){}//默认构造
vector() = default;//强制生成默认构造,这里写默认构造的原因就是如果你写了任意的构造,
//那么编译器就不会生成其它的默认构造,比如说你写了拷贝构造,那么编译器就不会生成无参的默认构造
vector(const vector<T>& v) {
reserve(v.size());
for (auto& e : v) {
push_back(e);
}
}
void clear() {//clear
_finish = _start;
}
//vector<T>& operator=(const vector<T>& v) {//重载=的传统写法
// if (this != &v) {//第一步是先清理空间,防止自己给自己赋值
// clear();
// reserve(v.size());
// for (auto& e : v) {
// push_back(e);
// }
// return *this;
// }
//}
void swap(vector<T>& v) {//swap
std::swap(_start, v._start);
std::swap(_finish, v._finish);
std::swap(_end_of_storage,v._end_of_storage);
}
vector<T>& operator=(vector<T> v) {//重载=的现代写法
swap(v);
return *this;
}
template <class InputIterator>
vector(InputIterator first,InputIterator last) {
while (first != last) {
push_back(*first);
++first;
}
}
vector(int n,const T& val = T()) {
reserve(n);
for (size_t i = 0; i < n; i++) {
push_back(val);
}
}
private:
iterator _start = nullptr;//这里初始化为nullptr目的就是减少构造函数的代码量
iterator _finish = nullptr;
iterator _end_of_storage = nullptr;
};
}

