Vector 类模板
关于vector类模板,有以下几个知识点值得注意:
1. vector不是一个类,而是一个模板,他可以用来储存同一个类型的多个对象,所以它也是一个容器。vector可以储存内置(built-in)类型也可存储自定义的类,形如vector<int>才是一个类,即vector模板要和一个类型相结合才是一个真正可用的类。
2. vector的初始化方法又以下几种:
vector<T> v1; //默认构造函数,初始化的v1为空vector
vector<T> v2(v1); //将v1中的元素全部复制到v2中,v1中对象的类必须与v2的类相同
vector<T> v3(n); //v3含有n个元素,依据元素类型有不同的初始化方法(见下面讲解)
vector<T> v4(n, v); //v4含有n个值为v的元素
如果vector不为空,则一定会对其中的元素初始化,在上面v3的情况中:
(1)若T为内置类型,如 int,则这n个元素都会被初始化为0;
(2)若T为自定义类型并含有默认的构造函数,则T会被按照构造函数进行初始化;
(3)如果不符合以上两种情况,在初始化vector时一定要指出如何初始化其中元素。
3. vector的长度是可以动态变化的,习惯于C/java的程序员可能倾向于给vector预分配合适的大小,但是C++更适合用于先构造一个空vector,然后动态加入元素,vector的内部实现可以管理内存分配,为程序员减少负担。
4. 与string类一样,vector<T>有成员函数size和empty,用来判断vector的大小。size的返回值是size_type型的。我们可以用脚标[ ]运算获得vector中的元素,例如下面的for循环
for (vector<int>::size_type i=0; i<ivec.size(); ++i)
{
ivec[i]=0;
}
这里又有一个值得注意的地方,就是在for loop里使用了size函数,这样在每次循环时都要调用这个函数,看似很费时,在C/java编程时可能更倾向于把size返回值赋予一个常数再用于循环。但事实上,由于vector大小是可以动态变化的,这样做存在不安全因素,每次用size检测当下的vector大小会更加合适,而且size函数是inline的(这里我还不太懂),所以在运行时调用这个函数并不会太费时。
另外,脚标运算无法获取vector中不存在的元素,如果要在vector中加入元素,应该用push_back(x)。
Iterator迭代器
这是我之前一直搞不懂的概念。每个标准库中的容器(container)都会定义一个迭代器,用于遍历其中的每一个元素(因此我觉得翻译成遍历器也许更好理解)。为了便于iterator的使用,每个容器还定义了begin和end函数,它们返回的是iterator类型,begin返回指向第一个元素的迭代器,而end返回的不是最后一个元素,而是最后一个元素的下一个不存在的元素位置,意味着当一个迭代器的值与end返回值相等时,即完成了所有元素的遍历。关于iterator的操作有以下几种:
1. dereference operator (*),即取迭代器所指元素的值,*ivec与ivec[0]的功能相同。
2. increment operator (++),即使迭代器移向下一个位置,类似于在for循环中使用int i时的++i,指向下一个元素, 示例如下:
for (vector<int>:: iterator iter=ivec.begin(); iter!=ivec.end(); ++iter)
{
*iter=0;
}
3. const_iterator,当你希望用迭代器对vector中元素只进行读操作的时候,可以用const_iterator,这样将无法修改迭代器所指元素的值:
for (vector<int>:: const_iterator iter=ivec.begin(); iter!=ivec.end(); ++iter)
{
*iter=0; //error: *iter is const
}
4. iterator当然也可以进行加减运算,比如 iter+n 即向后移动n个元素,iter-n即向前移动n个元素,iter2-iter1返回一个类型为difference_type的值,表示两个迭代器位置的差,是个带符号整数类型。