第三章 标准库string、vector、bitset类型
关于C++的几篇博客,参考人民邮电出版社的《C++ Primer 中文版》一书。
本章着重介绍标准库中的string、vector、bitset类。
第二节 标准库vector类及其迭代器
一、vector基本概念
1、vector是一种容器,是C++容器的一种,是同一种类型对象的集合。
2、vector是一个模板类(class template),每次定义时都需要用尖括号包含模板的指定类型才可以。
3、vector不是一种数据类型,而是一个模板类,vector<int>或者vector<string>的格式才表示一种数据类型。
4、重要概念 —— vector对象是动态增长的,也就是元素是可以动态增删的:
为了方便理解这个动态增长的概念,我们可以拿vector跟数组做比较。
数组:提前定义好所需要的内存空间大小,然后分配空间,然后对仅限于该空间范围内的内存进行读写操作。要想增加新的数组元素,是不可能在原数组上实现的。
vector:为了既保持连续的内存空间,又希望能动态增加元素,vector采用push_back()成员函数来实现。为了便于理解,可以简单直观地理解成:每push_back一次,都是重新开辟相应大小的连续空间,然后赋值。
二、vector对象的定义和初始化
vector<T> v1 | 默认构造函数输出为空 |
vector<T> v2(v1) | 将v2初始化为v1的一个副本,二者元素完全相同 |
vector<T> v3(n, i) | v3包含n个T类型取值为i的元素 |
vector<T> v4(n) | v4含有n个值初始化的元素(初始化值为多少取决于模板的数据类型。如果是int,则初始化为0;如果为string,则按照string类的默认构造函数来初始化,也就是每个元素都初始化为空字符串,依此类推;有些类不存在任何构造函数,那么就要求必须提供元素初始值) |
三、vector对象的操作
v.empty() | 如果v为空,则返回真 |
v.size() | 返回元素个数,返回类型为vector<int>::size_type |
v.push_back( t ) | 在v的末位增加一个值为t的元素 |
v[n] | 略,n的数据类型也为vector<int>::size_type,另:下标操作不能实现添加元素 |
v1 = v2 | 略 |
v1 == v2 | 略 |
!=, <, <=, >, >= | 略 |
vector<int>::size_type // right
vector::size_type // wrong
四、迭代器
标准库为每一种容器都定义了相应的迭代器类型。迭代器是一种检查容器内元素并遍历元素的数据类型。任何容器的迭代器都符合这个定义,满足这种属性,虽然不同容器的迭代器之间略有不同。那么问题来了,用下标不是可以直接访问容器元素的吗?干嘛还要定义迭代器这种东东?因为在标准库中,只有少数的容器才支持下标操作,而迭代器却适用于所有容器,也就是它提供了比下标操作更通用的方法。
1、基本定义:
vector<int>::iterator iter; // 定义一个对应于vector<int>这种类型的容器的迭代器对象,命名为iter。iterator是vector模板类的一个成员。
2、begin和end操作:
简单直观地,可以将iterator理解为可指向容器任意元素的指针。
vector<int> v;
vector<int>::iterator iter = v.begin(); // 如果v不为空,则iter指向v的第一个元素
iter = v.end(); // 如果v不为空,则iter指向v最后一个元素的下一个,即一个不存在的元素,end()只是为了告诉我们所有元素都已经处理完
// 如果v为空,则v.begin()和v.end()返回值相同
3、用迭代器访问容器元素:
之所以可以理解迭代器为指针,就在于,迭代器可以通过*操作符实现所指向位置的元素取值。
vector<int>::iterator iter = v.begin(); // iter指向v的第一个元素
*iter = 0; // 将第一个元素的值置为0
++iter; // iter现在指向第二个元素
int n = 5
iter = iter + n; // iter由第二个元素改为指向第七个元素
vector<int>::iterator iter2 = v.begin();
vector<int>::difference_type distance = iter2 - iter; // 计算两个迭代器的距离时,返回类型应为difference_type(signed类型)
例1:
// 用for循环将容器的每个元素设置为0
for(vector<int>::iterator iter = v.begin(); iter!=v.end(); ++iter)
*iter = 0;
4、const_iterator: 元素只读迭代器
例2:
for(vector<int>::const_iterator iter = v.begin(); iter!=v.end(); ++iter)
{
*iter = 0; // error
std::cout << *iter <<std::endl; // ok
}
例2总结:const_iterator类型的对象,其本身可以改变,但其指向的元素不可改变。
例3:
const vector<int>::iterator iter = v.begin();
*iter = 0; // ok
++iter; // error
例3总结:要区分const_iterator和const的iterator。与例2相反,const的iterator,其本身不可改变,但其指向的元素可以改变。
5、任何改变容器长度的操作都会使已存在的迭代器失效。究其原因,可以这样理解:我们上面说过容器的空间是动态增长的,当push_back增加新元素进去时,重新开辟新的数量的内存空间,然后把新旧元素都放在新的空间中。这时地址已经发生变化了,如果再用之前定义的迭代器,其指向的空间其实已经被释放掉了,无意义了。