首页 > 代码库 > 泛型编程与STL--各类容器迭代器失效的场景
泛型编程与STL--各类容器迭代器失效的场景
各类容器迭代器失效的场景:
其实在定义迭代器失效的时:在某些操作完成以后,认为这个迭代器指向的值有变化或者迭代器直接指向不合法的空间,都认为迭代器失效。只要不是指向操作之前的值都认为迭代器失效。
当要将元素安插于vector内,大小与容量之间的差别就变得格外重要。如果vector的大小等于其容量,安插新元素的唯一方法就是增加这个vector的内存总量,这意味得分配一块新的而且更大的内存,再将旧内存块的内容复制到新内存块中,然后归还旧内存块。
一旦vector的内存重新分配,其iterators便失效了。此外,在vector中间安插或删除元素,也会指向该安插点或删除点之后元素的所有iterators都失效。这表示如果你以member function reserve来原先分配内存,或如果所有安插或删除动作都在vector尾端进行,那么你就可以使vector的iterator不失效。
同时在vector内部是有复制构造函数和赋值构造函数的。
Vector<int> test_ww(vecc); //使用的是copy构造函数
Vector<int> test_ww;
Test_ww =vecc;//调用的是赋值构造函数
对于list来说:
是一个双向链表,能以O(1)时间在list开头、尾端、中间处安插及移除元素。List有一个重要心智,安插于结合(splice)动作不会造成指向list的iterators失效,即使是删除动作,也只会令指向被删除元素的那个iterator失效。这些iterators的顺序可能会被改变(也就是说在链表操作之后,一个list<T>::iterator可能拥有不同的前承元素与后继元素),但iterators自身不会被无效化或因此指向不同元素,除非明白执行无效化动作或目标物变换动作。
假设i是型别为vector<T>::iterator的一个有效iterator,当我们在i之前一个位置安插或移除一个元素,i会因此指向不同的元素,或者i根本完全失效。另一方面,假设i和j都是指向vector的iterators,存在某个整数n,使得I == j+n.这个情况下即使元素被安插在vector之中且i和j指向不同元素,两个指针的相互关系仍然维持不变。List正好相反,其iterators不会被无效化,也不会指向不同元素,但iterators的前承元素与后继元素的关系不会维持不变。
Deque使用说明:
Deque类似vector,常量事件内于尾端安插或移除元素,线性事件内于中间处安插或移除元素。
Deque和vector的差异在于,deque另外还提供常量事件内于序列开头新增或移除元素。在deque开头或尾端安插一个元素,需要O(1)的时间,在中间处安插元素的复杂度与N成线性关系,此处n是安插点距离deque开头语尾端的最短距离。
另一个差异是deque不具备类似vector的capacity()和reserve()之类的memberfunctions,也不提供任何与指向member functions相关之iterator有效性保证。取而代之的是,它保证“将元素安插于deque开头或尾端”不会造成deque中现存的任何元素被复制。对deque而言,类似vector内存重新分配的行为是不会发生的。
一般来说,insert(包括push_front与Push_back)会造成指向deque的所有iterators失效。对deque中调用erase也会造成指向deque的所有iterators失效。至于对着deque的开头或尾端调用erase(包括pop_front与pop_back),则会造成被移除元素的iterator失效。
Deque以动态区段化的数组实现出来。Deque通常内含一个表头,指向一组节点(nodes),每个节点包含固定数量并且连续存储的元素。当deque增长时便增加新的节点。
对于deque和list,内部都有push_back pop_back() push_front() pop_front()
但是对于vector,只有push_back()的 pop_back()
对于set来说:
和List一样,set具有数个重要性质:新元素的安插并不会造成既有元素的iterators失效,从set中删除元素也不会令任何iterators失效----当然被删除元素的iterator除外。因为set是有红黑树来实现的,内部也是以节点的形式存在的。
对于map来说:
和List一样,map具有一个重要性质:新元素的安插不会造成既有元素的iterators的失效。自map中删除元素也不会造成任何iterators失效---除了被删除元素的iterator外。
对于multiset来说:
和List一样,multiset具有数个重要性质:新元素的安插并不会造成既有元素的Iterators失效,从set中删除元素也不会令任何iterators失效—当然被移除元素的iterator除外。
对于multimap来说:
和List一样:multimap具有一个重要性质:新元素的安插不会造成既有元素的iterators的失效。自multimap中删除元素也不会造成任何iterators失效—除了被移除元素的iterator外。
对于priority_queue来说:
它是一个适配器,它实现于某个底部的container之上。缺省的地步型别是vector,但也可以明确指定不同的地步型别。它底部存储元素是用vector来实现的。但是带优先权的队列是标准的数据结构,可以不同方式实现出来。通常priority_queue是以heap来实现,并以算法make_heap、push_heap和pop_heap来维护heap的状态。也就是说优先级队列是有堆来实现的。
对于泛型编程,也就是运用模板,但是在STL中使用了迭代器作为粘合剂,使得容器和算法分离,这样就使得STL更好扩展,模板也是一种多态,属于静态多态,也即是编译期间的多态,在编译期间进行实例化。但是虚拟机制就是运行期多态。是在运行期间才知道在什么时候会被调用。
《泛型编程与STL》这本书讲述了泛型编程在STL中的使用,讲述了什么泛型编程,从使用C++的基本编程来实现自己的需求,然后根据需求再来更新技巧,引出迭代器这个概念,围绕迭代器这个概念来展开,讲述了迭代器的类型,从理论上来分析各种迭代器的属性。进而讲述了算法的重要性,将上述已经介绍过的迭代器引入算法中。引出了容器这个概念,使得迭代器成为容器和算法之间的粘合剂。
在各个容器的实现中定义对外接口,比如各种迭代器和迭代器的类别,这样算法就是根据这些属性来指向,包括为某些容器元素进行偏特化。(偏特化和特化的区别?比如已经定义了一个算法实现的模板,但是为了指针在顶一个模板,这个就是偏特化,但是为了int*这种指针定义一个模板就是特殊。偏特化为某一类重新定义模板(比如为指针这一类),但是特化是为某一个重新定义模板(比如Int*这个类型),在STL中有很多的偏特
对于vector:
成员函数有pop_back() push_back()
对于list deque:
成员函数有pop_front() push_front() push_back() pop_back()
对于slist:
成员函数有push_front() pop_front()
同时上述这几个容器也有insert()函数
但是上述这几个容器和关联容器的插入操作只有insert()函数
泛型编程与STL--各类容器迭代器失效的场景