一、vector简介
vector代表数组的序列容器,其大小可以改变。
也就是数据结构中能动态增容的顺序表
此文章将从两个方面开始:使用和实现(注意事项)
文章目录
二、vector的使用
1.构造函数
vs的编译器下无参的构造函数会将vector对象里面的size和capacity初始化为0;
拷贝构造:使用已经存在的对象构造另一个对象
使用迭代器区间初始化对象:将引用这段区间的值深拷贝到对象初始区间中
2.vector的遍历
#include <iostream>
#include <vector>
using namespace std;
int main()
{
vector<int> v1;
v1.push_back(1);
v1.push_back(2);
v1.push_back(3);
v1.push_back(4);
// 1、下表+[]
for (size_t i = 0; i < v1.size(); i++)
{
cout << v1[i] << ' ';
}
cout << endl;
// 2.迭代器
vector<int>::iterator it = v1.begin();
//
while (it != v1.end())
{
cout << *it << " ";
++it;
}
cout << endl;
// 3.范围for
for (auto e : v1)
{
cout << e << " ";
}
cout << endl;
}
3.vector的增容
增加 vector 的容量(即 vector 在不重新分配存储的情况下能最多能持有的元素的数量)到大于或等于 n 的值。如果n 大于当前的 capacity(),那么就会分配新存储,否则该方法不做任何事。
reserve() 不会更改 vector 的大小。
如果 n 大于 capacity(),那么所有迭代器,包含 end() 迭代器和所有到元素的引用都会失效。否则,没有迭代器或引用会失效。
在调用 reserve() 后,插入只会在它将导致 vector 的大小大于 capacity() 的值时触发重新分配。
我们看一下vs下的扩容机制:
#include <iostream>
#include <vector>
using namespace std;
int main()
{
vector<int> v1;
size_t sz = 0;
for (int i = 0; i < 100; i++)
{
v1.push_back(i);
if (sz != v1.capacity())
{
sz = v1.capacity();
cout << "sz = " << v1.capacity() << endl;
}
}
}
3.2 如果vector想尾插数据,开空间用reserve还是resize?——用reserve
我们先了解一下resize函数
重设vector容器大小以容纳 count 个元素,在 count == size() 时不做任何事。
如果当前大小大于count,那么减小容器到它的开头 count 个元素。
如果当前大小小于 count :
- 那么后附额外的默认插入的元素
- 那么后附额外的 value 的副本
如果知道要开多大空间且后续要进行尾插,就用reserve提前开好。
如果用resize就是开空间并初始化,比如v1.resize(100); 开100个int空间,全部初始化成0,如果再v1.push_back(i); 就是在这100个空间以后再加数据,因为前100个已经有数据了
4.vector的几个常用函数
1.容器中的元素数量
2.返回容器当前已为之分配空间的元素数
3.后附给定元素 value 到容器尾
如果新的 size() 大于 capacity(),那么所有迭代器和引用(包含 end() 迭代器)都会失效。否则只有 end() 迭代器会失效。
4.插入元素到容器中的指定位置
如果新 size() 大于旧 capacity() 就会导致重分配。 如果新的 size() 大于 capacity(),那么所有迭代器和引用都会失效。因为需要新开辟一段连续并且更大的capacity空间,然后将原来顺序表中的数据深拷贝到新的表中。否则,只有在插入点前的迭代器和引用会保持有效。end() 迭代器也会失效。(因为插入数据之后之前的end()位置是新插入元素的位置,所以vector内部实现的时候end()会后移一位)
5、从容器擦除指定的元素
返回值
最后移除元素之后的迭代器
- 如果 pos 指代末元素,那么返回 end() 迭代器。
- 如果在移除前 last == end(),那么返回更新的 end() 迭代器。
如果范围 [first, last) 为空,那么返回 last。
#include <iostream>
#include <vector>
using namespace std;
int main()
{
vector<int> v1;
v1.push_back(1);
v1.push_back(2);
v1.push_back(3);
v1.push_back(4);
v1.push_back(5);
vector<int>::iterator pos = v1.erase(v1.begin() + 2);
cout << *pos << endl;
}
这里删除的是v1[2],后面的元素往前移动,返回的是删除位置的迭代器,所以删除位置之后的迭代器都失效了,以为元素往前移动了一位
使位于擦除点或之后的迭代器失效,包含 end() 迭代器。
迭代器 pos 必须合法且可解引用,因此不能以 end() 迭代器(合法,但不可解引用)作为 pos 的值。
如果 first == last,那么迭代器 first 不需要可解引用:擦除空范围是无操作。
5.vector的几个迭代器
1.begin()
2.end()
三、模拟实现vector
还是老样子,先搭建整体框架
#include <iostream>
#include <assert.h>
namespace zxy_vector
{
template <class T>
class vector
{
public:
typedef T* iterator; // Vector的迭代器是一个原生指针
private:
iterator _start;
iterator _finish;
iterator _endofstorage;
};
}
先编写构造函数,这样就可以初始化对象了
vector()
:_start(nullptr) //指向第一个数据的起始位置
,_finish(nullptr) //指向最后一个数据的位置
,_endofstorage(nullptr) //存储容量的最后一个位置
{
}
//v2(v1)
vector(const vector<T>& v)
:_start(nullptr)
, _finish(nullptr)
, _endofstorage(nullptr)
{
reserve(v.capacity());
//for (auto& ch : v)
//{
// push_back(ch);
//}
auto it_begin = v.begin();
int i = 0;
while (it_begin != v.begin() + v.size())
{
_start[i++] = *it_begin;
it_begin++;
}
_finish = _start + i;
}
注意:写拷贝构造的时候一定要是深拷贝,因为模板参数不一定总是内置类型,还有可能是自定义类型比如说:string,所以我们一定要注意深拷贝的问题,不然调用析构函数的时候,同一个空间会释放两次,导致程序崩溃
接下来就可以写插入数据的接口了
void push_back(const T& val)
{
if (_finish == _endofstorage)
{
reserve(capacity() == 0 ? 4 : capacity() * 2);
}
*_finish = val;
_finish++;
}
完成reserve接口
void reserve(size_t n)
{
if (n > capacity())
{
T* temp = new T[n];
if (_start)
{
//将数据拷贝到新空间 深拷贝
auto it_begin = begin();
int i = 0;
while (it_begin != begin() + size())
{
temp[i++] = *it_begin;
it_begin++;
}
//释放旧空间
delete[] _start;
}
_finish = temp + size();
_start = temp;
_endofstorage = _start + n;
}
}
注意:这里的三个私有数据成员会有是失效的问题,因为reserve扩充之后的新空间是重新开辟的,三个指针还是指向旧空间的位置,所以我们要保存好各自之间的差值,以便更新指针
插入数据之后,就可以访问数据了
T& operator[](size_t pos)
{
assert(pos < size());
return _start[pos];
}
const T& operator[](size_t pos) const
{
assert(pos < size());
return _start[pos];
}
size_t capacity() const
{
return _endofstorage - _start;
}
size_t size() const
{
return _finish - _start;
}
iterator begin() const
{
return _start;
}
iterator end() const
{
return _finish;
}
这里添加const修饰this指针是因为:const修饰的对象不能调用非const成员函数,不能进行权限的放大;还有就是成员函数返回值返回的对象是临时对象,临时对象具有常属性,也就是const修饰的对象,所以我们通常使用返回值对象调用成员函数的时候,就只可以调用const修饰的成员函数
接下来就是编写可能导致迭代器失效的接口:
void insert(iterator pos, const T& val)
{
int len = pos - _start;
assert(pos <= _finish && pos >= _start);
//检查扩容
if (size() == capacity())
{
reserve(capacity() + 1);
}
pos = _start + len;
iterator it_end = _finish - 1;
while (it_end >= pos)
{
*(it_end + 1) = *it_end;
it_end--;
}
*pos = val;
// reserve之后容量指针已经发生改变,但是再插入数据之前_finish还是指向原来数据的下一个位置,等新添加数据之后,_finish指针需要后移一位
_finish++;
}
iterator erase(iterator pos)
{
int len = pos - _start;
assert(pos < _finish && pos >= _start);
iterator it_begin = pos;
while (it_begin < _finish - 1)
{
*it_begin = *(it_begin + 1);
it_begin++;
}
_finish--;
return _start + len;
}
vector.h
#include <iostream>
#include <assert.h>
namespace zxy_vector
{
template <class T>
class vector
{
public:
typedef T* iterator;
vector()
:_start(nullptr) //指向第一个数据的起始位置
,_finish(nullptr) //指向最后一个数据的位置
,_endofstorage(nullptr) //存储容量的最后一个位置
{
}
//v2(v1)
vector(const vector<T>& v)
:_start(nullptr)
, _finish(nullptr)
, _endofstorage(nullptr)
{
reserve(v.capacity());
//for (auto& ch : v)
//{
// push_back(ch);
//}
auto it_begin = v.begin();
int i = 0;
while (it_begin != v.begin() + v.size())
{
_start[i++] = *it_begin;
it_begin++;
}
_finish = _start + i;
}
//v1 = v2;
vector<T>& operator=(vector<T> temp)
{
std::swap(_start, temp._start);
std::swap(_finish, temp._finish);
std::swap(_endofstorage, temp._endofstorage);
return *this;
}
~vector()
{
delete[] _start;
_start = _finish = _endofstorage = nullptr;
}
void push_back(const T& val)
{
if (_finish == _endofstorage)
{
reserve(capacity() == 0 ? 4 : capacity() * 2);
}
*_finish = val;
_finish++;
}
T& operator[](size_t pos)
{
assert(pos < size());
return _start[pos];
}
const T& operator[](size_t pos) const
{
assert(pos < size());
return _start[pos];
}
void resize(size_t n, T val = T())
{
if (n <= size())
{
_finish = _start + n;
}
else
{
reserve(n);
while (_finish != _start + n)
{
*_finish = val;
_finish++;
}
}
}
void reserve(size_t n)
{
if (n > capacity())
{
T* temp = new T[n];
if (_start)
{
//将数据拷贝到新空间
auto it_begin = begin();
int i = 0;
while (it_begin != begin() + size())
{
temp[i++] = *it_begin;
it_begin++;
}
//释放旧空间
delete[] _start;
}
_finish = temp + size();
_start = temp;
_endofstorage = _start + n;
}
}
void insert(iterator pos, const T& val)
{
int len = pos - _start;
assert(pos <= _finish && pos >= _start);
//检查扩容
if (size() == capacity())
{
reserve(capacity() + 1);
}
pos = _start + len;
iterator it_end = _finish - 1;
while (it_end >= pos)
{
*(it_end + 1) = *it_end;
it_end--;
}
*pos = val;
// reserve之后容量指针已经发生改变,但是再插入数据之前_finish还是指向原来数据的下一个位置,等新添加数据之后,_finish指针需要后移一位
_finish++;
}
iterator erase(iterator pos)
{
int len = pos - _start;
assert(pos < _finish && pos >= _start);
iterator it_begin = pos;
while (it_begin < _finish - 1)
{
*it_begin = *(it_begin + 1);
it_begin++;
}
_finish--;
return _start + len;
}
size_t capacity() const
{
return _endofstorage - _start;
}
size_t size() const
{
return _finish - _start;
}
iterator begin() const
{
return _start;
}
iterator end() const
{
return _finish;
}
private:
iterator _start;
iterator _finish;
iterator _endofstorage;
};
}
测试代码
#include "vector.h"
using namespace std;
//#include <vector>
void test_vector1()
{
zxy_vector::vector<int> v1;
v1.push_back(1);
v1.push_back(2);
v1.push_back(3);
v1.push_back(4);
for (int i = 0; i < v1.size(); i++)
{
cout << v1[i] << ' ';
}
cout << endl;
v1.insert(v1.begin(), 10);
v1.insert(v1.begin() + 2, 30);
v1.insert(v1.begin() + v1.size(), 60);
for (auto c : v1)
{
cout << c << ' ';
}
cout << endl;
v1.erase(v1.begin());
v1.erase(v1.begin() + 1);
v1.erase(v1.begin() + v1.size() - 1);
for (auto c : v1)
{
cout << c << ' ';
}
cout << endl;
}
void test_vector2()
{
zxy_vector::vector<int> v1;
v1.push_back(2);
v1.push_back(2);
v1.push_back(3);
v1.push_back(4);
v1.push_back(5);
zxy_vector::vector<int>::iterator begin = v1.begin();
while (begin < v1.end())
{
if (*begin % 2 == 0)
{
v1.erase(begin);
}
else
{
begin++;
}
}
for (auto c : v1)
{
cout << c << ' ';
}
cout << endl;
}
void test_vector3()
{
zxy_vector::vector<string> v1;
v1.push_back("111111");
v1.push_back("111111");
v1.push_back("111111");
v1.push_back("111111");
v1.push_back("111111");
v1.push_back("111111");
v1.push_back("111111");
v1.push_back("111111");
v1.push_back("111111");
v1.push_back("111111");
v1.push_back("111111");
v1.push_back("111111");
for (auto c : v1)
{
cout << c << ' ';
}
cout << endl;
}
void test_vector4()
{
zxy_vector::vector<int> v1;
v1.push_back(1);
v1.push_back(2);
v1.push_back(3);
v1.push_back(4);
zxy_vector::vector<int> v2(v1);
for (auto c : v1)
{
cout << c << ' ';
}
cout << endl;
for (int i = 0; i < v2.size(); i++)
{
cout << v2[i] << ' ';
}
cout << endl;
}
void test_vector5()
{
zxy_vector::vector<int> v1;
v1.push_back(1);
v1.push_back(2);
v1.push_back(3);
v1.push_back(4);
zxy_vector::vector<int> v2;
v2 = v1;
for (auto c : v1)
{
cout << c << ' ';
}
cout << endl;
for (auto c : v2)
{
cout << c << ' ';
}
cout << endl;
}
int main()
{
test_vector5();
return 0;
}
四、 std::vector::at 和 std::vector::operator[]
at() 函数和 [] 运算符的重载,两者都可以得到相应下标的值。
当发生越界行为时:
at 是抛异常;
operator[] 不进行边界检查,通过此运算符访问不存在的元素是未定义行为。