「C++」vector的使用及接口模拟详解
目录
前言
vector的使用
模拟实现
成员变量
reserve扩容
insert插入
erase删除
迭代器失效总结
resize
构造
默认构造和拷贝构造
迭代器区间构造
n个参数val的构造
赋值重载
模拟实现时的其他问题
typename语法
内置类型的构造函数
类模板中的类名简写
后记
附:完整代码
前言
大家好呀~欢迎来到海盗猫的CPP专栏——vector篇
本篇我们将以string的学习基础为对比,讲解vector的不同点和模拟实现中遇见的各种问题
vector是一个类模板,必需要显示实例化vector<typename>
vector的使用,与string相同的接口使用方法类似,但也有不同,可以查询文档
vector文档:
vector - C++ Reference
vector的使用
1.reserve规定不会缩小容量
在string中,reserve操作当n小于size时,不同的编译器会有不同的处理方法,vs2022中,reserve只会进行扩容;g++4.8则会缩小容量到有效字符的长度;
这是因为string规定中,没有明确写明这种情况的处理方式,所以不同的编译器可以有不同的处理方法;
而在vector的文档中查询可以看到

当n大于capacity容量时,才会扩容,其他情况均不会产生影响
2.resize会删除数据
当参数n小于当前size时会执行删除数据的操作,将第n个数据后的数据删除;
n大于capacity则会扩容到n
3.vector没有实现流插入和流提取的重载
这是由于vector能存储各种不同的类型,包括自定义类型,所以输出方式不尽相同,需要我们自行输出
4.insert,erase函数只有使用迭代器调用的重载(存在迭代器失效问题,后文模拟实现提及)

模拟实现
由于从vector容器开始,将使用类模板来进行其模拟实现,且成员变量替换为三个迭代器类型,所以模拟实现会出现许多不同的地方和注意点,据情况做出详细的解释;
成员变量
在string的模拟实现中三个成员变量为
char* _str = nullptr;//指向数据存储的空间地址
size_t _size = 0;//有效字符串长度
size_t _capacity = 0;//容量大小,不包括'\0'
而到了vector,三个成员变量将使用iterator的迭代器类型来声明:
public:typedef T* iterator;
private:iterator _start = nullptr;//有效空间开始位置的指针iterator _finish = nullptr;//有效空间结束位置的指针iterator _end_of_storage = nullptr;//容量最后位置的指针
reserve扩容
注意使用old_size临时变量来辅助更新迭代器
void reserve(size_t n){if (n > capacity()) {size_t old_size = size();T* tmp = new T[n];memmove(tmp, _start, old_size * sizeof(T));//第三参数为移动的空间字节数delete[] _start;_start = tmp;//_finish = _start + size();//若这时调用size(),由于_start已经更新为新地址,而此时的_finish还指向原来的就空间,//将会导致size()结果出错,进而导致此处_finish的更新出错//因此使用变量old_size,保存原本的size长度,再与已经更新的_start相加来得到更新后的_finish_finish = _start + old_size;_end_of_storage = _start + n;} }
此时还有一个比较难以发现的缺陷,实例:
当我们使用string来测试扩容
可以看到代码运行错误了,内置类型并不会出现这种错误
这是因为:
string中我们知道,成员变量中有指针类型;而此时,我们reserve中使用的使memmove来复制数据,而memmove的拷贝是以字节为单位的浅拷贝。
浅拷贝将导致拷贝后的指针仍然指向原本的位置,随后delete[] _start;将指向的空间释放,致使tmp变为野指针,进而_start也为野指针;且我们的测试代码放在一个函数中,在结束函数之前,对象会自动调用析构函数释放空间,这将导致_start指向的空间被析构两次,导致出现错误
所以拷贝数据的方法需要改变:
//II.使用for循环调用[]来进行数据的拷贝,此时即便为自定义类型,也会调用对应的[]和赋值重载,由此实现深拷贝void reserve(size_t n) {if (n > capacity()) {size_t old_size = size();T* tmp = new T[n];for (size_t i = 0; i < old_size; i++){tmp[i] = _start[i];}delete[] _start;_start = tmp;//_finish = _start + size();//若这时调用size(),由于_start已经更新为新地址,而此时的_finish还指向原来的就空间,将会导致size()结果出错,进而导致此处_finish的更新出错//因此使用变量old_size,保存原本的size长度,再与已经更新的_start相加来得到更新后的_finish_finish = _start + old_size;_end_of_storage = _start + n;}}此时即便为自定义类型,也会调用对应的[]和赋值重载,由此实现深拷贝,来让tmp指向构造出的新空间。
insert插入

对于vector容器,insert插入时,使用迭代器进行插入;
- 由于insert插入牵扯到扩容机制,若调用insert时产生了扩容,调用对象的成员变量都已经指向新空间,而pos仍然指向旧空间;
迭代器失效的版本:
//I.//由于扩容机制的存在,扩容后成员变量全体都指向了新空间,而此处参数pos还指向旧空间void insert(iterator pos, const T& x) {assert(pos >= _start);assert(pos <= _finish);//等于_finish时为尾插//扩容if (_finish == _end_of_storage) {reserve(capacity() == 0 ? 4 : capacity() * 2);}//挪动数据iterator end = _finish - 1;//若pos迭代器没有更新,将导致迭代器失效//原因是:pos还指向原来旧空间的位置,而end是新空间_finish - 1的来的迭代器,大小关系无法确定while (end >= pos) {*(end + 1) = *end;--end;}*pos = x;++_finish;}
- 此处为第一种迭代器失效:此时pos相当于一个野指针
解决办法为:
记录pos的相对位置,扩容完成后,再使用新_start来更新其指向
解决迭代器失效:
//II.//修正pos迭代器,防止迭代器失效void insert(iterator pos, const T& x) {assert(pos >= _start);assert(pos <= _finish);//等于_finish时为尾插//扩容if (_finish == _end_of_storage) {size_t len = pos - _start;reserve(capacity() == 0 ? 4 : capacity() * 2);//reserve扩容之后,由于存储空间的转变,pos迭代器失效,需要重置pos指向pos = _start + len;}//挪动数据iterator end = _finish - 1;//若pos迭代器没有更新,将导致迭代器失效//原因是:pos还指向原来旧空间的位置,而end是新空间_finish - 1的来的迭代器,大小关系无法确定while (end >= pos) {*(end + 1) = *end;--end;}*pos = x;++_finish;}
此时测试代码:
void test_vector1() {vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);print_container(v);cout << v.size() << endl;cout << v.capacity() << endl;//查找数字x,在其位置上插入一个数字,并将原本该位置的数字*=10int x;cin >> x;//vector<int>::iteratorauto p = std::find(v.begin(), v.end(), x);if (p != v.end()) {//insert返回插入数据的pos位置//p意义已经改变(迭代器失效)//原本p为指向x的迭代器,插入后,p已经指向了插入的数据的位置,此时p不能直接访问v.insert(p, 40);(*p) *= 10;//p = v.insert(p, 40);//(*(p + 1)) *= 10;}print_container(v);cout << v.size() << endl;cout << v.capacity() << endl;
}
输入x=2,此时结果为:
可见,我们原意为,在x=2的位置插入新数字,并将2*=10,但结果错误,原因为:
insert时产生了扩容,虽然在内部我们更新了形参pos的指向,但形参不影响实参,所以此时外部访问实参p,p仍然指向旧空间,此时迭代器失效,所以访问不到数字2的位置,导致修改失败;
- 所以insert函数还需要将形参更新后的指向,传回给实参,因此给insert加上返回值,返回插入数据空间更新后的迭代器(地址);
返回形参pos更新后的内容:
//III.
//使insert返回pos形参的新指向,用于更新参数pos在该函数体外部的实参的指向,防止外部的实参还指向旧空间,致使迭代器失效
//ps:不能通过将形参pos设置为引用来解决pos实参的更新问题,因为使用&,即(iterator& pos),将导致参数为v.begin()+1这一类时出错,因为此时参数为一个具有常性的临时变量;
//这个问题也不能通过给引用加const,即(const iterator& pos)来解决,因为这样pos在insert函数内就不能修改了
iterator insert(iterator pos, const T& x) {assert(pos >= _start);assert(pos <= _finish);//等于_finish时为尾插//扩容if (_finish == _end_of_storage) {size_t len = pos - _start;reserve(capacity() == 0 ? 4 : capacity() * 2);//reserve扩容之后,由于存储空间的转变,pos迭代器失效,需要重置pos指向pos = _start + len;}//挪动数据iterator end = _finish - 1;//若pos迭代器没有更新,将导致迭代器失效//原因是:pos还指向原来旧空间的位置,而end是新空间_finish - 1的来的迭代器,大小关系无法确定while (end >= pos) {*(end + 1) = *end;--end;}*pos = x;++_finish;return pos;
}
此时将将测试代码插入语句修改为p = v.insert(p, 40);(*(p + 1)) *= 10;再测试:

即可获得正确的结果
- 此处也引出了第二种迭代器失效:
即,insert以后,即便insert返回更新了实参p,但其实际意义也已经改变,所以我们使用了(*(p + 1)) *= 10;来修改x=2的值,使其*=10;
而在我们自己实现的insert中,若没有产生扩容,此时p即便没有通过返回值更新,也可以通过直接(*(p + 1)) *= 10;来对x=2修改(因为没有异地扩容,还是指向原来的位置),此时并不会报错,但实际上p的含义也已经改变(原本实参p是用来指向x=2这个数据的);
但在std::vector中,不论insert有没有扩容,迭代器实际有没有指向错误,编译器都会默认直接报错,以防止访问失效的迭代器,此时就必须更新迭代器才能继续访问;
erase删除
由于vector类模板可以接受许多不同类型以及自定义类型的数据,所以erase也只能使用迭代器来删除数据,且不再像string中提供len参数一样来删除指定长度;
vector中erase默认删除单个元素,或者删除某个迭代器区间(左闭右开)的元素;
由于erase删除也涉及了数据的挪动,所以erase和insert一样,执行操作后,都默认将导致迭代器失效(VS直接报错);
即和insert同理,即便在我们模拟实现的erase中,不存在内存空间的修改时,直接访问更新前的迭代器,理论上可以做到同样的修改操作,但在VS的std::vector中,由于严格检查,即便迭代器本身没有错误,也会认为其迭代器失效,必须要更新才可以访问使用。
- 单元素删除erase模拟代码
iterator erase(iterator pos) {assert(pos >= _start);assert(pos < _finish);iterator it = pos + 1;while (it < _finish) {*(it - 1) = *it;++it;}--_finish;//返回删除位置的迭代器,即被删除元素在删除前的下一个位置的迭代器return pos;}
erase返回值为被删除元素或区间,在被删除前的指向下一个位置元素的迭代器
- 但由于erase会把删除位置的后续元素往前挪动,所以实际上pos位置的迭代器直接就是指向目标位置的
void test_vector2() {vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);//删除所有的偶数print_container(v);auto it = v.begin();while (it != v.end()){if ((*it % 2) == 0) {it = v.erase(it);//模拟实现没有严格的检查,即便此处it没有更新,也不会报错//但若改为std::vector,此处若没有it来接收返回更新后的迭代器,就会直接报错}else {//因为erase会返回删除元素被删除前的下一位元素的迭代器,所以it只有在没有调用erase时才++it++;}}print_container(v);
}
- 区间删除erase
iterator erase(iterator begin, iterator end) {assert(begin >= _start, begin < _finish);assert(end >= _start, end < _finish);size_t len = end - begin;iterator it = end;while (it < _finish) {*(it - len) = *it;++it;}_finish -= len;return begin;}
迭代器失效总结
VS环境下严格检测,出现迭代器失效都会默认报错;而在;linux的g++环境下,没有出现实际错误的迭代器就不会报错
相当于野指针
对内存进行修改之后,都认为迭代器失效(reserve中有讲)
迭代器意义改变
insert后,由于对象内部的内容改变,所以insert之后默认参数迭代器的意义已经改变,所以使用时都不要直接访
问,VS下访问会直接报错(不管这个迭代器参数实际上有没有)
erase同理,删除数据后,其 实参迭代器的意义也会改变,也会默认报错
string中也有迭代器失效,只是在我们之前的使用中,大多insert和erase都直接使用通过下标来操作的重载,而很少使用迭代器的重载版本
resize
由于vector的模拟实现使用类模板,且不能确定vector存储数据的类型,所以此处参数val的缺省值不能给类似数字0,或者字符'\0'一类的数据,而是使用模板类型T类型的默认构造函数,通过构造函数来构造一个匿名对象用于缺省值。
void resize(size_t n, T val = T()) {if (n < size()) {_finish = _start + n;}else {reserve(n);while (_finish < _start + n) {*_finish = val;++_finish;}}}
但原本内置类型是不存在构造的,将导致如果T为此时为内置类型,将无法兼容这里的用法
由此,cpp中引入了内置类型的构造和析构函数的概念,内置类型也可以像自定义类型一样,使用默认构造来初始化变量int a(0);int b(1);
构造
前文中,我们没有书写构造函数,代码也可以正常运行,这是因为编译器在类没有构造函数时,会自动生成一个无参构造函数,且成员变量不论是否在构造函数中显示构造,都会自动进行初始化列表,所以前文中的三个迭代器都使用了默认值nullptr来进行了构造
默认构造和拷贝构造
vector() = default;//强制生成默认构造//直接使用范围for与尾插来拷贝数据vector<T>(const vector<T>& v) {reserve(v.size());for (auto i : v) {push_back(i);}}
迭代器区间构造
使用迭代器区间来构造
template <class InputIterator>vector(InputIterator first, InputIterator last) {while (first != last) {push_back(*first);++first;}}
此处使用函数模板来实现,这是因为,当传入的迭代器区间不是vector,但数据类型与调用构造的对象相同时,也使其可以构造出该对象
list<int> i(5, 2);
vector<int> v1(i.begin(),i.end());//使用list的迭代器区间来构造vector
若不使用模板,而是直接使用vector内的iterator类型就会导致出错
n个参数val的构造
使用n个val值来构造
//n个val构造vector(size_t n, const T& val = T()) {reserve(n);for (size_t i = 0; i < n; i++){push_back(val);}}
测试代码:

当只传参数n时,代码正常运行
此时出现了意外情况:

当我们传入参数val值时,却出现了错误,且报错信息显示错误点是在迭代器区间的构造函数中:

这说明此处函数调用就出现了错误,其原因为:
1.)我们知道,编译器会自动匹配与传参类型最相近的函数来调用;
2.)而上述情况中,由于编译器对于整型默认判断为int类型,由于我们的参数n为size_t类型,这里就会产生隐式类型转换,且val为int导致两个参数类型不同;
3.)而对于迭代器区间的构造函数模板,此处两个参数传值都为int类型其类型相同,模板函数中俩个参数类型也相同,可直接推导为int,所以对于编译器来说,迭代器区间的构造函数显然更符合所传参数的类型;
解决方法很简单,将参数n类型换位int即可,因为此时对于这种特殊情况来说,就有了符合两个参数都为int的函数,这样也不会去使用迭代器区间构造的函数模板了(当有符合条件的函数存在时,不会使用函数模板来推导出一个新函数)
vector(int n, const T& val = T()) {reserve(n);for (size_t i = 0; i < n; i++){push_back(val);}}
赋值重载
赋值重载与string中一样,使用自定义的swap函数,来直接交换底层的迭代器指向;而赋值重载的参数则使用传值传参,过程中直接就会调用拷贝构造来进行深拷贝出一个新对象
void swap(vector<T>& v) {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;}
模拟实现时的其他问题
typename语法
详见笔记 模板初阶:函数模板 模块
template <class T>
void print_vector(const vector<T>& v) {//规定,没有实例化的类模板里取东西,编译器区分其实际意义//此处const_iterator,编译器就不能自动分别其为类型,还是一个静态成员变量//所以手动使用typename来告诉编译器,其为类型;或者使用auto直接使用v.begin()推导其类型//vector<T>::const_iterator it = v.begin();typename vector<T>::const_iterator it = v.begin();//auto it = v.begin();while (it != v.end()) {cout << *it << ' ';++it;}
}
内置类型的构造函数
为了兼容自定义类型在传参时使用默认构造来实现缺省值,cpp中将内置类型也加入了构造概念,可以像类一样,用使用构造的方法来定义内置类型
int a(0);int b(1);int c = int();//匿名对象double d = double();
类模板中的类名简写
在类模板中,使用类型名称时,可以直接用类名来替代如vector<T>在类模板内部,就可以直接使用vector来替代
例如:
vector(const vector& v) {reserve(v.size());for (auto i : v) {push_back(i);}}vector<T>(const vector<T>& v) {reserve(v.size());for (auto i : v) {push_back(i);}}
后记
本期对于vector的学习就到这里了,文中只对常用的接口进行了解释和模拟,有错误的地方感谢大家指出,我们下期再见~
本期专栏:C++_海盗猫鸥的博客-CSDN博客
个人主页:海盗猫鸥-CSDN博客

附:完整代码
vector.h
#pragma once
#include<iostream>
#include<assert.h>
#include<vector>
namespace hdmo {template<class T>class vector{public:typedef T* iterator;typedef const T* const_iterator;vector<T>() = default;//强制生成默认构造vector(const vector& v) {reserve(v.size());for (auto i : v) {push_back(i);}}//vector<T>(const vector<T>& v) {// reserve(v.size());// for (auto i : v) {// push_back(i);// }//}//迭代器区间构造//vector(iterator first, iterator last) {// while (first != last) {// push_back(*first);// ++first;// }//}template <class InputIterator>vector<T>(InputIterator first, InputIterator last) {while (first != last) {push_back(*first);++first;}}//n个val构造//vector(size_t n, const T& val = T()) {// resize(n);// //reserve(n);// for (size_t i = 0; i < n; i++)// {// push_back(val);// }//}vector<T>(int n, const T& val = T()) {reserve(n);for (size_t i = 0; i < n; i++){push_back(val);}}void swap(vector<T>& v) {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;}~vector<T>() {if (_start) {delete[] _start;_start = _finish = _end_of_storage = nullptr;}}//迭代器iterator begin(){return _start;}iterator end(){return _finish;}const_iterator begin() const{return _start;}const_iterator end() const{return _finish;}//错误示范,[]返回应为对应位置的对象,而不是指针//iterator operator[](size_t pos) {// assert(pos < size());// return _start + pos;//}T& operator[](size_t pos) {assert(pos < size());return _start[pos];}const T& operator[](size_t pos) const {assert(pos < size());return _start[pos];}//I.memmove移动数据,存在缺陷//这是因为,memmove函数是以字节为单位拷贝数据的,但对于自定义类型,存在指针时。就会出现浅拷贝的情况,导致memmove拷贝的指针和原指针指向同一个地址,导致delete[]释放空间后,出现野指针//void reserve(size_t n) {// if (n > capacity()) {// size_t old_size = size();// T* tmp = new T[n];// memmove(tmp, _start, old_size * sizeof(T));//第三参数为移动的空间字节数// delete[] _start;// _start = tmp;// //_finish = _start + size();// //若这时调用size(),由于_start已经更新为新地址,而此时的_finish还指向原来的就空间,将会导致size()结果出错,进而导致此处_finish的更新出错// //因此使用变量old_size,保存原本的size长度,再与已经更新的_start相加来得到更新后的_finish// _finish = _start + old_size;// _end_of_storage = _start + n;// }//}//II.使用for循环调用[]来进行数据的拷贝,此时即便为自定义类型,也会调用对应的[]和赋值重载,由此实现深拷贝void reserve(size_t n) {if (n > capacity()) {size_t old_size = size();T* tmp = new T[n];for (size_t i = 0; i < old_size; i++){tmp[i] = _start[i];}delete[] _start;_start = tmp;//_finish = _start + size();//若这时调用size(),由于_start已经更新为新地址,而此时的_finish还指向原来的就空间,将会导致size()结果出错,进而导致此处_finish的更新出错//因此使用变量old_size,保存原本的size长度,再与已经更新的_start相加来得到更新后的_finish_finish = _start + old_size;_end_of_storage = _start + n;}}size_t size() const{return _finish - _start;}size_t capacity() const{return _end_of_storage - _start;}void clear(){_finish = _start;}bool empty() const{return (size() == 0);}void push_back(const T& x) {if (_finish == _end_of_storage) {reserve(capacity() == 0 ? 4 : capacity() * 2);}*_finish = x;++_finish;}void pop_back() {assert(!empty());//为空不能删除--_finish;}/*//I.//由于扩容机制的存在,扩容后成员变量全体都指向了新空间,而此处参数pos还指向旧空间void insert(iterator pos, const T& x) {assert(pos >= _start);assert(pos <= _finish);//等于_finish时为尾插//扩容if (_finish == _end_of_storage) {reserve(capacity() == 0 ? 4 : capacity() * 2);}//挪动数据iterator end = _finish - 1;//若pos迭代器没有更新,将导致迭代器失效//原因是:pos还指向原来旧空间的位置,而end是新空间_finish - 1的来的迭代器,大小关系无法确定while (end >= pos) {*(end + 1) = *end;--end;}*pos = x;++_finish;}//II.//修正pos迭代器,防止迭代器失效void insert(iterator pos, const T& x) {assert(pos >= _start);assert(pos <= _finish);//等于_finish时为尾插//扩容if (_finish == _end_of_storage) {size_t len = pos - _start;reserve(capacity() == 0 ? 4 : capacity() * 2);//reserve扩容之后,由于存储空间的转变,pos迭代器失效,需要重置pos指向pos = _start + len;}//挪动数据iterator end = _finish - 1;//若pos迭代器没有更新,将导致迭代器失效//原因是:pos还指向原来旧空间的位置,而end是新空间_finish - 1的来的迭代器,大小关系无法确定while (end >= pos) {*(end + 1) = *end;--end;}*pos = x;++_finish;}*///III.//使insert返回pos形参的新指向,用于更新参数pos在该函数体外部的实参的指向,防止外部的实参还指向旧空间,致使迭代器失效//ps:不能通过将形参pos设置为引用来解决pos实参的更新问题,因为使用&,即(iterator& pos),将导致参数为v.begin()+1这一类时出错,因为此时参数为一个具有常性的临时变量;//这个问题也不能通过给引用加const,即(const iterator& pos)来解决,因为这样pos在insert函数内就不能修改了iterator insert(iterator pos, const T& x) {assert(pos >= _start);assert(pos <= _finish);//等于_finish时为尾插//扩容if (_finish == _end_of_storage) {size_t len = pos - _start;reserve(capacity() == 0 ? 4 : capacity() * 2);//reserve扩容之后,由于存储空间的转变,pos迭代器失效,需要重置pos指向pos = _start + len;}//挪动数据iterator end = _finish - 1;//若pos迭代器没有更新,将导致迭代器失效//原因是:pos还指向原来旧空间的位置,而end是新空间_finish - 1的来的迭代器,大小关系无法确定while (end >= pos) {*(end + 1) = *end;--end;}*pos = x;++_finish;return pos;}iterator erase(iterator pos) {assert(pos >= _start);assert(pos < _finish);iterator it = pos + 1;while (it < _finish) {*(it - 1) = *it;++it;}--_finish;return pos;}iterator erase(iterator begin, iterator end) {assert(begin >= _start, begin < _finish);assert(end >= _start, end < _finish);size_t len = end - begin;iterator it = end;while (it < _finish) {*(it - len) = *it;++it;}_finish -= len;return begin;}void resize(size_t n, T val = T()) {if (n < size()) {_finish = _start + n;}else {reserve(n);while (_finish < _start + n) {*_finish = val;++_finish;}}}private:iterator _start = nullptr;//有效空间开始位置的指针iterator _finish = nullptr;//有效空间结束位置的指针iterator _end_of_storage = nullptr;//容量最后位置的指针};
}
测试代码:
#include"vector.h"
#include<list>
#include<string>
using std::list;
using std::string;
using namespace hdmo;
using std::cout;
using std::cin;
using std::endl;
template <class T>
void print_vector(const vector<T>& v) {//规定,没有实例化的类模板里取东西,编译器区分其实际意义//此处const_iterator,编译器就不能自动分别其为类型,还是一个静态成员变量//所以手动使用typename来告诉编译器,其为类型;或者使用auto直接使用v.begin()推导其类型//vector<T>::const_iterator it = v.begin();typename vector<T>::const_iterator it = v.begin();//auto it = v.begin();while (it != v.end()) {cout << *it << ' ';++it;}//for (size_t i = 0; i < v.size(); i++)//{// cout << v[i] << ' ';//}//for (auto i : v) {// cout << i << ' ';//}cout << endl;
}template <class Container>
void print_container(const Container& v) {auto it = v.begin();while (it != v.end()) {cout << *it << ' ';++it;}cout << endl;
}void test_vector1() {std::vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);//v.push_back(4);print_container(v);cout << v.size() << endl;cout << v.capacity() << endl;//cout << v.empty();//v.pop_back();//vector<int>::iterator it1 = v.insert(v.begin(), 8);//vector<int>::iterator it2 = v.insert(v.end(), 0);//cout << it1 - v.begin() << endl;//cout << it2 - v.begin() << endl;//print_vector(v);//查找数字x,在其位置上插入一个数字,并将原本该位置的数字*=10int x;cin >> x;//vector<int>::iteratorauto p = std::find(v.begin(), v.end(), x);if (p != v.end()) {//insert返回插入数据的pos位置//p意义已经改变(迭代器失效)//原本p为指向x的迭代器,插入后,p已经指向了插入的数据的位置,此时p意义改变// v.insert(p, 40);//(*p) *= 10;v.insert(p, 40);(*(p + 1)) *= 10;}print_container(v);cout << v.size() << endl;cout << v.capacity() << endl;
}void test_vector2() {//std::vector<int> v;vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);//删除所有的偶数//print_container(v);//auto it = v.begin();//while (it != v.end())//{// if ((*it % 2) == 0) {// it = v.erase(it);// }// else {// it++;// }//}//print_container(v);//cout << v.size() << endl;//cout << v.capacity() << endl;//v.erase(v.begin(), v.end());////v.erase(v.end() - 1, v.end());//print_container(v);//cout << v.size() << endl;//cout << v.capacity() << endl;v.resize(5, 0);v.reserve(20);print_container(v);cout << v.size() << endl;cout << v.capacity() << endl;v.resize(10, 3);v.reserve(20);print_container(v);cout << v.size() << endl;cout << v.capacity() << endl;v.resize(25, 7);v.reserve(20);print_container(v);cout << v.size() << endl;cout << v.capacity() << endl;
}
void test_vector3() {//int a(0);//int b(1);//int c = int();//匿名对象//cout << a << b << c << endl;////double d = double();vector<int> v1;v1.resize(5, 1);vector<int> v2(v1);vector<int> v3;v3 = v2;print_container(v2);print_container(v3);
}
void test_vector4() {//list<int> i(5, 2);//vector<int> v2;//v2.push_back(1);//v2.push_back(2);//v2.push_back(3);//v2.push_back(4);//vector<int> v1(i.begin(),i.end());//vector<int> v1(v2.begin(),v2.end() - 1);//print_container(v1);//vector<int> v3(10);//print_container(v3);vector<char> v4(10,'x');print_container(v4);v4.push_back('y');print_container(v4);//vector<string> v5(10,"xxx");//print_container(v5);//v5.push_back("yyy");//print_container(v5);
}
int main() {test_vector4();return 0;
}




