目录
前言
大家好呀~欢迎来到海盗猫的CPP专栏------vector篇
本篇我们将以string的学习基础为对比,讲解vector的不同点和模拟实现中遇见的各种问题
vector是一个类模板,必需要显示实例化vector<typename>
vector的使用,与string相同的接口使用方法类似,但也有不同,可以查询文档
vector文档:
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的模拟实现中三个成员变量为
cpp
char* _str = nullptr;//指向数据存储的空间地址
size_t _size = 0;//有效字符串长度
size_t _capacity = 0;//容量大小,不包括'\0'
而到了vector,三个成员变量将使用iterator的迭代器类型来声明:
cpp
public:
typedef T* iterator;
private:
iterator _start = nullptr;//有效空间开始位置的指针
iterator _finish = nullptr;//有效空间结束位置的指针
iterator _end_of_storage = nullptr;//容量最后位置的指针
reserve扩容
注意使用old_size临时变量来辅助更新迭代器
cpp
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指向的空间被析构两次,导致出现错误
所以拷贝数据的方法需要改变:
cpp//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仍然指向旧空间;
迭代器失效的版本:
cpp
//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来更新其指向
解决迭代器失效:
cpp
//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;
}
此时测试代码:
cpp
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,在其位置上插入一个数字,并将原本该位置的数字*=10
int x;
cin >> x;
//vector<int>::iterator
auto 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更新后的内容:
cpp
//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模拟代码
cpp
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位置的迭代器直接就是指向目标位置的
cpp
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
cpp
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类型的默认构造函数,通过构造函数来构造一个匿名对象用于缺省值。
cpp
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来进行了构造
默认构造和拷贝构造
cpp
vector() = default;//强制生成默认构造
//直接使用范围for与尾插来拷贝数据
vector<T>(const vector<T>& v) {
reserve(v.size());
for (auto i : v) {
push_back(i);
}
}
迭代器区间构造
使用迭代器区间来构造
cpp
template <class InputIterator>
vector(InputIterator first, InputIterator last) {
while (first != last) {
push_back(*first);
++first;
}
}
此处使用函数模板来实现,这是因为,当传入的迭代器区间不是vector,但数据类型与调用构造的对象相同时,也使其可以构造出该对象
cpp
list<int> i(5, 2);
vector<int> v1(i.begin(),i.end());//使用list的迭代器区间来构造vector
若不使用模板,而是直接使用vector内的iterator类型就会导致出错
n个参数val的构造
使用n个val值来构造
cpp
//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的函数,这样也不会去使用迭代器区间构造的函数模板了(当有符合条件的函数存在时,不会使用函数模板来推导出一个新函数)
cpp
vector(int n, const T& val = T()) {
reserve(n);
for (size_t i = 0; i < n; i++)
{
push_back(val);
}
}
赋值重载
赋值重载与string中一样,使用自定义的swap函数,来直接交换底层的迭代器指向;而赋值重载的参数则使用传值传参,过程中直接就会调用拷贝构造来进行深拷贝出一个新对象
cpp
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语法
详见笔记 模板初阶:函数模板 模块
cpp
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中将内置类型也加入了构造概念,可以像类一样,用使用构造的方法来定义内置类型
cpp
int a(0);
int b(1);
int c = int();//匿名对象
double d = double();
类模板中的类名简写
在类模板中,使用类型名称时,可以直接用类名来替代如vector<T>在类模板内部,就可以直接使用vector来替代
例如:
cpp
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
cpp
#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;//容量最后位置的指针
};
}
测试代码:
cpp
#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,在其位置上插入一个数字,并将原本该位置的数字*=10
int x;
cin >> x;
//vector<int>::iterator
auto 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;
}



