1 for(vector<Enemy*>::iterator it = enemy_vector_.begin();it != enemy_vector_.end();++it) {
2 enemy_vector_.erase(it);
3 //CCLog("%d num",enemy_vector_.size());
4 it = enemy_vector_.begin();
5 }
vector进行erase后旧的容器会被重新整理成一个新的容器,it迭代器失效,变成一野指针。所以erase后,要赋给it一个新的迭代器。
回过头来看上面写的代码明显有问题,erase 后又跳到第一个,但是++it,所以再erase时就漏掉了一个。修改后:
1 bool EnemyManager::RemoveEnemies() {
2 vector<Enemy*>::iterator it = enemy_vector_.begin();
3 while(it != enemy_vector_.end()) {
4 if((*it)->IsOutofMap()) {
5 delete *it;
6 enemy_vector_.erase(it);
7 it = enemy_vector_.begin();
8 } else {
9 ++it;
10 }
11 }
12 return true;
13 }
用vector时,出现了些问题,系统学习下:
vector<T> v1; vector保存类型为T对象;默认构造函数v1为空。
vector<T> v2; v2是v1的一个副本。
vector<T> v3(n,i); v3包含n个值为i的元素。
vector<T> v4(n); v4含有值初始化的元素的n个副本。
关键概念:vector对象动态增长
vector对象的重要属性就在于可以在运行时高效地添加元素。因为vector增长的效率高,在元素值已知的情况下,最好是动态地添加元素。这种增长方式不同于c语言中的内置数据类型,也不同于大多数其他编程语言的数据类型。虽然可以对给定元素个数的vector对象预先分配内存,但更有效的方法是先初始化一个空vector对象,然后再动态地增加元素。
vector操作
v.empty()如果v为空,则返回true,否则返回false。
v.size()返回v中元素的个数。
v.push_back(t)在v的末尾增加一个值为t的元素。
v[n]返回v中位置为n的元素。
v1=v2把v1的元素替换为v2中元素的副本。
v1==v2如果v1与v2相等,则返回true。
!=,<,<=,> and >=保持这些操作惯有的的涵义。
vector对象的size
empty和size操作类似于string的相关操作。成员函数size返回相应vector类定义的size_type的值。使用size_type类型时,必须指出该类型是在哪里定义的。vector类型总是包括vector的元素类型。
例:vector<int>::size_type
向vector添加元素
push_back操作接受一个元素值,并将它作为一个新的元素添加到vector对象后面。
vector的下表操作
vector中的对象是没有命名的,可以按vector中对象的位置来访问它们。通常使用下标操作符来获取元素。vector的小标操作类似于string类型的下表操作。
vector的下表操作符接受一个值,并返回vector中该对应位置的元素。vector元素的位置从0开始。下例使用for循环把vector中的每个元素值都重置为0:
for(vector<int>::size_type ix = 0; ix != ivec.size(); ++ix)
ivec[ix] = 0;
和string类型的下标操作符一样,vector下标操作的结果为左值,因此可以像循环体中所做的那样实现写入。另外,和string对象的下标操作类型,这里用size_type类型作为vector下标的类型。上例中,即使ivec为空,for循环也会正确执行。ivec为空则调用size返回0,并且for中的测试比较ix和0。第一次循环时,由于ix本身就是0,则条件测试失败,for循环一次也不执行。
下标操作不添加元素(必须是已存在的元素才能用下标操作符索引。通过下标操作进行赋值时,不会添加任何元素)
初学c++的程序员可能会认为vector的小标操作可以添加元素,其实不然:
vector<int> ivec; // empty vector
for(vector<int>::size_type ix = 0; ix != 10; ++ix)
ivec[ix] = ix;
关键概念:安全的反省编程
习惯于c或java编程的c++程序员可能会觉得难以理解,for循环的判断条件用 != 而不是用<来测试vector下标值是否越界。c程序员难以理解的还有,上例中没有在for循环之前就调用size成员函数并保存其返回的值,而是在for语句头中调用size成员函数。
C++程序员习惯于优先选用!=而不是<来编写循环判断条件。
调用size成员函数而不保存它返回的值,在这个例子中同样不是必需的,但这反映了一中良好的编程习惯。在c++中,有些数据结构如vector也可以动态增长。
像size这样的小库函数几乎都定义为内联函数,所以每次循环过程中调用它的运行代价是比较小的。
迭代器简介
除了使用下标来访问vector对象的元素外,标准库还提供了另一种访问元素的方法:使用迭代器(iterator)。迭代其是一种检查容器内元素并遍历元素的数据类型。
标准库为每一种标准容器定义了一种迭代器类型。迭代器类型提供了比下标操作更通用化的方法:所有的标准库容器都定义了相应的迭代器类型,而只有少数的容器支持下标操作。因为迭代器对所有容器都适用,现代C++程序更倾向于使用迭代器而不是下标操作访问容器元素,即使对支持下标操作的vector类型也是这样。
容器的iterator类型
每种容器类型都定义了自己迭代器类型,如vector:
vector<int>::iterator iter;
这符语句定义了一个名为iter的变量,它的数据类型是vector<int>定义的iterator类型。每个标准库类型都定义了一个名为iterator的成员,这里的iterator与迭代器实际类型的含义相同。
术语:迭代器和迭代器类型
程序员首次遇到有关迭代器的术语时可能会困惑不解,原因之一是由于同一个术语iterator往往表示两个不同的事物。一般意义上指的是迭代器的概念;而具体而言时指的是由容器定义的具体的iterator类型,如vector<int>。
begin和end操作
每种容器都定义了一对命名为begin和end的函数,用于返回迭代器。如果容器中有元素的话,由begin返回迭代器指向第一个元素:
vector<int>::iterator iter = ivec.begin();
上述语句把iter初始化为由名为vector操作返回的值。假设vector不空,初始化后,iter即指该元素为ivec[0]。
由end操作返回的迭代器指向vector的“末端元素的下一个”。“超出末端迭代器(off-the-end iterator)”表明它指向了一个不存在的元素。如果vector为空,begin返回的迭代器与end返回的迭代器相同。
由end操作返回的迭代器并不指向vector中任何实际的元素,相反,它只是起一个哨兵的作用,表示我俄美女已经处理完vector中所有的元素。
vector迭代器的自增和解引用运算
迭代器类型定义了一些操作来获取迭代器所指向的元素,并允许程序员将迭代器从一个元素移动到另一个元素。
迭代器类型可使用解引用操作符(*)来访问迭代器所指向的元素:
*iter = 0;
迭代器使用自增操作符向前移动迭代器指向容器中下一个元素。从逻辑上说,迭代器的自增操作和int类型对象的自增操作类似。对int对象来说,操作结果就是把int类型值“加1”,而对象则是把容器中的迭代器中的迭代器“向前移动一个位置” 因此,如果iter指向第一个元素,则++iter直线个第二个元素。
由于end操作返回的迭代器不指向任何元素,因此不能对它进行解引用或自增操作。
迭代器的其他操作
== != 来比较两个迭代器,如果两个迭代器对象指向同一个元素,则它们相等,否则就不想等。
const_iterator,该类型只能用于读取容器内元素,但不能改变其值。
write by fgd