第三章 STL
vector的介绍
vector是一个动态增长的数组,使用的是同一块连续的空间,当空间不足就会涉及到空间增容
优点:支持下标的随机访问。
缺点:数据过多时头插和中部插入的效率很低。
默认成员函数
_start; // 指向第一个位置
_finish; // 指向最后一个的下一个位置
// _finish - _strat = size。
_endofstorage; // 指向开辟空间的最后一个的下一个位置
关于空间的方法
与string的方法名字和功能一样
方法名 | 功能 |
---|---|
size | 获取数据个数 |
capacity | 获取容量大小 |
empty | 判断是否为空 |
resize | 即会改变空间也会改变数据 |
reserve | 开空间,只改变空间(容量)大小 |
reserve只负责开辟空间,如果知道需要多少空间,reserve可以缓解vector多次增容的代价
resize在空间的同时还会进行初始化,影响size。
不自己手动开空间,在空间不足时。vs下使用的STL基本是按照1.5倍方式扩容。linux下使用的STL基本是按照2倍方式扩容
迭代器
vector
和 string
的 迭代器
本质上就是原生指针,比较简单,但后续容器的 迭代器
就比较复杂了
正向迭代器
// 可读可写
vector<int>::iterator it = v1.begin();
while (it != v1.end())
{
cout << *it << ' ';
it++;
}
cout << endl;
反向迭代器
// 可读可写
vector<int>::reverse_iterator rit = v1.rbegin();
while (rit != v1.rend())
{
cout << *rit << ' ';
rit++;
}
begin()
和 end()
适用于 正向迭代器
begin()
为对象中的首个有效元素地址end()
为对象中最后一个有效元素的下一个地址
rbegin()
和 rend()
适用于 反向迭代器
rbegin()
为对象中最后一个有效元素地址rend()
为对象中首个有效元素的上一个地址
不能混用
const迭代器
void print_vector(const vector<int>& v)
{
vector<int>::const_iterator it = v.begin(); // 因为是const对象
while (it != v.end())
{
cout << *it << ' ';
it++;
}
cout << endl;
//vector<int>::const_reverse_iterator rit = v.rbegin(); // 反向的
}
对于 const
对象,还有 cbegin()
、cend()
和 crbegin()
、crend()
,当然这些都是 C++11
中新增的语法
对于 const
对象,存在重载版本,如 begin() const
,也就是说,const
修饰的对象也能正常使用 begin()
和 end()
、rbegin()
和 rend()
;C++11
中的这个新语法完全没必要,可以不用,但不能看不懂
find和sort
vector没有自己的find方法
如果要查找一个指定的值,需要调用算法库中的find。
vector<int>::iterator pos = find(v1.begin(), v1.end(), 66);
该函数使用 operator== 将各个元素与 val 进行比较。 此函数模板的行为等同于:
template<class InputIterator, class T>
InputIterator find (InputIterator first, InputIterator last, const T& val)
{
while (first!=last) {
if (*first==val) return first;
++first;
}
return last;
}
返回值:
返回指向范围内比较等于 val 的第一个元素的迭代器。 如果没有元素匹配,函数返回最后一个(最后一个并没有值,是一个指针,指向的是一个没有值的空间)。
// 算法库中排序
sort(v1.begin(), v1.end()); // 排序 // 默认为升序
增删改查
增删改查 | 功能 |
---|---|
push_back | 尾插 |
pop_back | 尾删 |
insert | 插在指定的位置处,参数是迭代器 |
erase | 删除指定的位置,参数是迭代器 |
vector<int> v1;
v1.push_back(55);
v1.push_back(2);
v1.push_back(66);
v1.push_back(4);
v1.push_back(10);
v1.insert(v1.begin(), 2); // 头插
v1.erase(v1.begin()); // 头删
v1.erase(v1.begin()+3);
print_vector(v1); // 55 2 66 10
删除指定的数
// vector没有find接口,这是算法库中的
vector<int>::iterator pos = find(v1.begin(), v1.end(), 66);
if (pos != v1.end())
{
//cout << *pos;
v1.erase(pos);
}
迭代器失效
第一种情况
vector<int>::iterator it = v1.begin();
v1.push_back(10); // 迭代器的使用过程中不能添加数据
v1.push_back(10); // 可以理解为因为增容开辟了新空间。原来的地址失效了
while (it != v1.end())
{
cout << *it;
it++;
}
第二种情况
vector<int>::iterator it = v1.begin();
// 要求删除偶数
while (it != v1.end())
{
if (*it % 2 == 0)
{
// 删除之后it就失效了,因为it的位置不对了
//v1.erase(it); // 不能这么写
it = v1.erase(it); // erase会返回删除位置的下一个
}
else
{
it++;
}
}
for (auto& i : v1)
{
cout << i <<' ';
}
交换和清理
vector<int> v1 = { 1,2,3 };
vector<int> v2 = { 4,5,6 };
v1.swap(v2); //交换两个对象
v1.clear();
v2.clear(); //清空
swap
函数:
- 这个函数实现原理不同
std::swap
,std::swap
实际在交换时,需要调用多次拷贝构造和赋值重载函数,对于深拷贝来说,效率是很低的 - 而
vector::swap
在交换时,交换是三个成员变量,因为都是指针,所以只需要三次浅拷贝交换,就能完美完成任务 - 实际在
vector::swap
内部,还是调用了std::swap
,不过此时是高效的浅拷贝
void swap(vector<T>& v)
{
std::swap(_start, v._start);
std::swap(_finish, v._finish);
std::swap(_endofstorage, v._endofstorage);
}
clear
函数:
- 令
_finish
等于_start
,就完成了清理,不需要进行缩容,这样做是低效的
void clear()
{
_finish = _start;//不能置空,会发现内存泄漏
}