C++vector遍历一个容器,排序后删除重复项 vectors iterator 遍历数组not dereferencable

c++ vector&string& 用sort排序后 如何比较 两个vector&string& 是否相等
[问题点数:40分,结帖人chinacaicheng]
c++ vector&string& 用sort排序后 如何比较 两个vector&string& 是否相等
[问题点数:40分,结帖人chinacaicheng]
不显示删除回复
显示所有回复
显示星级回复
显示得分回复
只显示楼主
相关帖子推荐:
2013年6月 Linux/Unix社区大版内专家分月排行榜第二2013年5月 Linux/Unix社区大版内专家分月排行榜第二2013年3月 Linux/Unix社区大版内专家分月排行榜第二2013年1月 Linux/Unix社区大版内专家分月排行榜第二2012年12月 Linux/Unix社区大版内专家分月排行榜第二2012年8月 Linux/Unix社区大版内专家分月排行榜第二2011年12月 Linux/Unix社区大版内专家分月排行榜第二2011年10月 C/C++大版内专家分月排行榜第二2011年10月 Linux/Unix社区大版内专家分月排行榜第二
2012年6月 C/C++大版内专家分月排行榜第三2012年6月 PHP大版内专家分月排行榜第三2012年5月 C/C++大版内专家分月排行榜第三2012年3月 Linux/Unix社区大版内专家分月排行榜第三2012年2月 Linux/Unix社区大版内专家分月排行榜第三2011年11月 C/C++大版内专家分月排行榜第三
2010年5月 C/C++大版内专家分月排行榜第三2010年3月 C/C++大版内专家分月排行榜第三2010年1月 C/C++大版内专家分月排行榜第三
本帖子已过去太久远了,不再提供回复功能。VS2010 Debug模式下运行时出现 vector iterator not dereferencable
[问题点数:20分]
VS2010 Debug模式下运行时出现 vector iterator not dereferencable
[问题点数:20分]
不显示删除回复
显示所有回复
显示星级回复
显示得分回复
只显示楼主
相关帖子推荐:
2012年7月 C/C++大版内专家分月排行榜第二2012年6月 C/C++大版内专家分月排行榜第二
本帖子已过去太久远了,不再提供回复功能。2873人阅读
vector是C++标准模板库中的部分内容,它是一个多功能的,能够操作多种数据结构和算法的模板类和函数库。vector之所以被认为是一个容器,是因为它能够像容器一样存放各种类型的对象,简单地说,vector是一个能够存放任意类型的动态数组,能够增加和压缩数据。
为了可以使用vector,必须在你的头文件中包含下面的代码:
#include &vector&
vector属于std命名域的,因此需要通过命名限定,如下完成你的代码:
using std::
vector&int& vI
&或者连在一起,使用全名:
std::vector&int& vI
&建议使用全局的命名域方式:usingnamespace
c.assign(beg,end)
c.assign(n,elem)
将[ end)区间中的数据赋值给c。
将n个elem的拷贝赋值给c。
传回索引idx所指的数据,如果idx越界,抛出out_of_range。
传回最后一个数据,不检查这个数据是否存在。
传回迭代器中的第一个数据地址。
c.capacity()
返回容器中数据个数。
移除容器中所有数据。
判断容器是否为空。
指向迭代器中的最后一个数据地址。
c.erase(pos)
c.erase(beg,end)
删除pos位置的数据,传回下一个数据的位置。
删除[beg,end)区间的数据,传回下一个数据的位置。
传回第一个数据。
get_allocator
使用构造函数返回一个拷贝。
c.insert(pos,elem)
c.insert(pos,n,elem)
c.insert(pos,beg,end)
在pos位置插入一个elem拷贝,传回新数据位置。
在pos位置插入n个elem数据。无返回值。
在pos位置插入在[beg,end)区间的数据。无返回值。
c.max_size()
返回容器中最大数据的数量。
c.pop_back()
删除最后一个数据。
c.push_back(elem)
在尾部加入一个数据。
c.rbegin()
传回一个逆向队列的第一个数据。
传回一个逆向队列的最后一个数据的下一个位置。
c.resize(num)
重新指定队列的长度。
c.reserve()
保留适当的容量。
返回容器中实际数据的个数。
c1.swap(c2)
swap(c1,c2)
将c1和c2元素互换。
同上操作。
vector&Elem& c
vector&Elem& c1(c2)
vector &Elem& c(n)
vector &Elem& c(n, elem)
vector &Elem& c(beg,end)
c.~ vector &Elem&()
创建一个空的vector。
复制一个vector。
创建一个vector,含有n个数据,数据均已缺省构造产生。
创建一个含有n个elem拷贝的vector。
创建一个以[end)区间的vector。
销毁所有数据,释放内存。
operator[]
返回容器中指定位置的一个引用。
创建一个vector
vector容器提供了多种创建方法,下面介绍几种常用的。
创建一个Widget类型的空的vector对象:
vector&Widget& vW
创建一个包含500个Widget类型数据的vector:
vector&Widget& vWidgets(500);
&创建一个包含500个Widget类型数据的vector,并且都初始化为0:
vector&Widget& vWidgets(500, Widget(0));
&创建一个Widget的拷贝:
vector&Widget& vWidgetsFromAnother(vWidgets);
向vector添加一个数据
vector添加数据的缺省方法是push_back()。push_back()函数表示将数据添加到vector的尾部,并按需要来分配内存。例如:向vector&Widget&中添加10个数据,需要如下编写代码:
for(int i= 0;i&10; i++)
&&& vWidgets.push_back(Widget(i));
获取vector中制定位置的数据
vector里面的数据是动态分配的,使用push_back()的一系列分配空间常常决定于文件或一些数据源。如果想知道vector存放了多少数据,可以使用empty()。获取vector的大小,可以使用size()。例如,如果想获取一个vector
v的大小,但不知道它是否为空,或者已经包含了数据,如果为空想设置为-1,你可以使用下面的代码实现:
int nSize = v.empty() ? -1 :static_cast&int&(v.size());
访问vector中的数据
使用两种方法来访问vector。
1、&& vector::at()
2、&& vector::operator[]
operator[]主要是为了与C语言进行兼容。它可以像C语言数组一样操作。但at()是我们的首选,因为at()进行了边界检查,如果访问超过了vector的范围,将抛出一个例外。由于operator[]容易造成一些错误,所有我们很少用它,下面进行验证一下:
分析下面的代码:
vector&int&
v.reserve(10);
for(int i=0; i&7; i++)
&&& v.push_back(i);
&int iVal1 = v[7];&// not bounds checked - will not throw
&int iVal2 = v.at(7); // bounds checked - will throw if out of range
catch(const exception& e)
&cout && e.what();
删除vector中的数据
vector能够非常容易地添加数据,也能很方便地取出数据,同样vector提供了erase(),pop_back(),clear()来删除数据,当删除数据时,应该知道要删除尾部的数据,或者是删除所有数据,还是个别的数据。
Remove_if()算法&如果要使用remove_if(),需要在头文件中包含如下代码::
#include &algorithm&
&&&& Remove_if()有三个参数:
1、&& iterator _First:指向第一个数据的迭代指针。
2、&& iterator _Last:指向最后一个数据的迭代指针。
3、&& predicate _Pred:一个可以对迭代操作的条件函数。
条件函数&条件函数是一个按照用户定义的条件返回是或否的结果,是最基本的函数指针,或是一个函数对象。这个函数对象需要支持所有的函数调用操作,重载operator()()操作。remove_if()是通过unary_function继承下来的,允许传递数据作为条件。
例如,假如想从一个vector&CString&中删除匹配的数据,如果字串中包含了一个值,从这个值开始,从这个值结束。首先应该建立一个数据结构来包含这些数据,类似代码如下:
#include &functional&
enum findmodes
&FM_INVALID = 0,
&FM_STARTSWITH,
&FM_ENDSWITH,
&FM_CONTAINS
typedefstruct tagFindStr
&CString szMatchS
typedef FindStr* LPFINDSTR;
然后处理条件判断:
class FindMatchingString
&&& : public std::unary_function&CString,bool&
&FindMatchingString(const LPFINDSTR lpFS) : m_lpFS(lpFS) {}
&bool operator()(CString& szStringToCompare)const
&&&& bool retVal =false;
&&&& switch(m_lpFS-&iMode)
&&&& case FM_IS:
&&&&&&&& retVal = (szStringToCompare == m_lpFDD-&szMatchStr);
&&&&&&&& break;
&&&& case FM_STARTSWITH:
&&&&&&&& retVal = (szStringToCompare.Left(m_lpFDD-&szMatchStr.GetLength())
&&&&&&&&&&&&&& == m_lpFDD-&szWindowTitle);
&&&&&&&& break;
&&&& case FM_ENDSWITH:
&&&&&&&& retVal = (szStringToCompare.Right(m_lpFDD-&szMatchStr.GetLength())
&&&&&&&&&&&&&& == m_lpFDD-&szMatchStr);
&&&&&&&& break;
&&&& case FM_CONTAINS:
&&&&&&&& retVal = (szStringToCompare.Find(m_lpFDD-&szMatchStr) != -1);
&&&&&&&& break;
&&&& return retV
&&& LPFINDSTR m_lpFS;
通过这个操作你可以从vector中有效地删除数据:
fs.iMode = FM_CONTAINS;
fs.szMatchStr = szR
vs.erase(std::remove_if(vs.begin(), vs.end(), FindMatchingString(&fs)), vs.end());
&&& Remove(),remove_if()等所有的移出操作都是建立在一个迭代范围上的,不能操作容器中的数据。所以在使用remove_if(),实际上操作的时容器里数据的上面的。
看到remove_if()实际上是根据条件对迭代地址进行了修改,在数据的后面存在一些残余的数据,那些需要删除的数据。剩下的数据的位置可能不是原来的数据,但他们是不知道的。
调用erase()来删除那些残余的数据。注意上面例子中通过erase()删除remove_if()的结果和vs.enc()范围的数据。
使用::std::vector&&作为管理动态数组的优先选择
摘要: 本文介绍了C++标准库中的容器类vector,分析了它的优点,并且建议在应用程序中使用它作为动态数组的优先选择,
而不是MFC的CArray&&等其他类模板。最后介绍了vector的接口和使用时的注意事项。
在一些使用 MFC 的程序中,经常看到许多程序使用 CArray&&,由于 CArray&&的设计问题,造成使用它的代码的复杂化,增加了维护难度。
因此建议使用 ::std::vector&& 代替 CArray&&。
另外,也看到一些程序在用 malloc/realloc/free/new[]/delete[] 等手工管理内存。
在应用程序中,手工管理内存是容易导致错误的,应该用 ::std::vector&& 之类的对象来管理动态数组。
由于 MSDN 中关于 ::std::vector 的内容较少,我们在这里做一些介绍,供参考。
不熟悉 CArray&&/WIN32 也没关系,这里提到它们的地方并不太多。
1. CArray&& VS ::std::vector&& ?
CArray&& 和 ::std::vector&& 一样,都是模板类,用于管理任意类型的对象的动态数组。都在解构时释放所管理的动态内存。
因此都可以用于代替手工动态数组管理。
但是,CArray&& 是在 C++ 标准化之前很多年(VC++2.0时代)设计的,当时对 C++程序设计,面向对象程序设计,
模板程序设计等技术认识严重不足,尤其是当时对面向对象技术的错误信仰与宣传,造成 CArray&& 的设计有重大错误。
在 C++ 语言标准化以后(1998),以及 VC++ 6.0 出世以后,提供了标准的::std::vector&& 模板,
基本上在任何方面都要优于 CArray&&。Microsoft 由于要支持老的程序,因此一直保留了 CArray&&,
但显然并没有打算按照新的思想去发展它(至少应该提供operator=(CArray const&吧)。
概括起来,CArray&& 与 ::std::vector&& 有以下不同:
1) CArray&& 是 MFC 中的,::std::vector&& 存在于任何标准的 C++ 实现中。因此,你用熟了 CArray&& 也只能在 MFC 中用,
若用熟了 ::std::vector&&,你可以在任何平台的任何 C++ 编译器下使用。使用标准的部件也有利于别人理解你的程序。
CArray&& 继承了 CObject,仅仅为了实现 serialization,这是不恰当的,
违反了 &You don't pay for what you don't use.& 的 C++ 设计原则。
::std::vector&& 没有继承任何东西,只是实现了管理一个动态数组该做的事。
2) CArray&& 不是一个恰当的值类型,例如下列操作都是不合法的:
CArray&int,int&
CArray&int,int& b(a);& // error, must use Copy().
b =&&&&&&& // error, must use Copy().
b ==&&&&&& // error, you must write your own.
b &&&&&&&& // error, you must write your own.
与 CArray&& 相反,::std::vector&& 是一个认真设计的值类型,天生是可以拷贝构造和可赋值的。
如果 T 是可比较的,那么 ::std::vector&T& 将自动地是可以比较的。
此外,由于涉及到四个特殊成员函数;
T(); // 缺省构造函数(default constructor)
~T(); // 解构函数(destructor)
T( T const& ); // 拷贝构造函数
T& operator=( T const& ); // 拷贝赋值函数
的自动生成,如果使用 CArray() 作为 T 的成员变量,那么上述的四个特殊函数中的后两个将无法自动生成,需要手工写:
&& T( T const& t )
&&&&&& a_.Copy( t.a_ );
&&&&&& i_ = t.i_;
&&&&&& d_ = t.d_;
&&&&&& s_ = t.s_;
&& T& operator = ( T const& t )
&&&&&& if( this != &t )
&&&&&&&&&& a_.Copy( t.a_ );
&&&&&&&&&& i_ = t.i_;
&&&&&&&&&& d_ = t.d_;
&&&&&&&&&& s_ = t.s_;
&&&&&& return *
&& CArray&int,int& a_;
&& int i_;
&& double d_;
&& ::std::string s_;
如果使用 ::std::vector&&:
&& ::std::vector&int& a_;
&& int i_;
&& double d_;
&& ::std::string s_;
上面列出的三个特殊成员函数都不需要写。好处是明显的:当你增减 T 的成员变量时,你不必到
T(T const& 和 operator=() 中去相应地增减。
3) 没有现成的算法可以对 CArray&& 进行操作,而标准 C++ 里的标准算法大多都可以直接在
::std::vector&& 上运行。例如:
static int const init_vals[] = { 3, 1, 4, 1, 6, 9 };
vector&int& a( init_vals, init_vals + 6 ;
*find( a.begin(), a.end(), 6 ) = 5;&&& // 把6改成5
sort( a.begin(), a.end() );&&& // 排序。
可以说,CArray&& 的主要设计错误是把一个本来应该是一个简单的“值”类型的东西设计成一个难用的“对象”类型了。
所有的“值”的好特性都丧失了,但那些从CArray&&继承的派生类呢?
CByteArray等的问题与 CArray&& 的问题一样,甚至更多(例如,CPtrArray,永远不要用)。
同样,其他的 MFC container 模板,象 CMap&&, CList&& 等,都有类似问题,都应该用
::std::map&&,::std::list&& 等设计更好的东西代替。
2. ::std::vector&& 在哪里?
::std::vector&& 在头文件 &vector& 中定义:
(注意,标准的 C++ 头文件都没有 .h 后缀,有 .h 的文件是与 C 兼容的,或支持老的不标准的东西,象 &iostream.h&。)
namespace std
&&& template&typename T, typename A = allocator&T& &
&&& struct vector
&&&&&&& // 具体内容稍后讨论
&&& template&typename T, typename A&
&&&&&&& bool operator == ( vector&T,A& const& a, vector&T,A& const&&&& b );
&&& template&typename T, typename A&
&&&&&&& bool operator != ( vector&T,A& const& a, vector&T,A& const&&&& b );
&&& template&typename T, typename A&
&&&&&&& bool operator & ( vector&T,A& const& a, vector&T,A& const&&&& b );
&&& template&typename T, typename A&
&&&&&&& bool operator &= ( vector&T,A& const& a, vector&T,A& const&&&& b );
&&& template&typename T, typename A&
&&&&&&& bool operator & ( vector&T,A& const& a, vector&T,A& const&&&& b );
&&& template&typename T, typename A&
&&&&&&& bool operator &= ( vector&T,A& const& a, vector&T,A& const&&&& b );
vector&& 定义在 namespace std 中,使用时为了减少击键次数,通常使用一个类型定义缩短类型名称:
#include &vector&
typedef ::std::vector&int& IntV
IntVector b( a );
assert( a == c );
请注意 &vector& 中定义了六个 vector&T,A& 的比较函数。这些函数只在真的用到时才会被实例化,才会要求 T 也提供 operator==() 和 operator&()。
另外,A = alloctor&T&:用于提供一个用户定义的存储管理类。由于这个参数很少用到,而且在 VC++6 的实现中有问题,不能用,
因此以下的讨论忽略这一部分的内容。
3. ::std::vector&& 中的类型定义
vector&& 中定义了一些类型,下面只列出常用的:
typedef T value_
typedef T0
typedef T1 const_
typedef T2 reverse_
typedef T3 const_reverse_
value_type 就是 vector&T& 的元素类型,也就是 T。当写通用的算法处理任意类型的 vector&& 或其他容器类型时是很有用的。
iterator/const_iterator 是两个 vector&& 的实现定义的未知类型,用于访问vector&& 中的元素,类似于 T*/T const* 指针,他们的区别是一个指向的元素可被修改,另一个只可以读:
typedef ::std::vector&int& IntV
IntVector::
IntVector::const_iterator c_
++ iter++; // ok: increment, post-increment.
-- iter--; // ok: decrement, post-decrement.
++c_ c_iter++; // ok: increment, post-increment.
--c_ c_iter--; // ok: decrement, post-decrement.
*iter = 123; // ok.
int k = * // ok.
k = *--c_ // ok.
*c_iter = // error.
c_iter = // ok: iterator is convertible to const_iterator.
iter = c_ // error: can't convert const_iterator to iterator.
在使用上 iterator/const_iterator 和 T*/T const* 基本相同,事实上有些vector&& 的实现里就是用 T*/T const* 实现 iterator/const_iterator 的,但又不可以把 iterator/const_iterator 当作真正的 T*/T const*:
T* p = // may fail to compile.
T const* q = c_ // may fail to compile.
reverse_iterator/const_reverse_iterator 与 iterator/const_iterator 类似,但以相反的次序(从尾至头)访问 vector 中的元素。
各种各样的 iterator 在 STL 中有特别重要的意义,但这里我们不做具体介绍。只要理解通过 iterator 可以访问 vector 中的元素,大概相当于一个指示位置的指针就行了。
4. ::std::vector&& 的构造
vector&& 提供了以下构造函数:(忽略 allocator 参数)
vector( size_t n, T const t=T() );
vector( vector const & );
vector( const_iterator first, const_iterator last );
1) vector();
构造一个空的 vector,不包含任何元素。
IntVector v1; // 空的整数向量。
2) vector( size_t n, T const t=T() ;
构造一个 n 个相同元素 t 组成的 vector。如果不给出 t,那么将用 T() 做缺省值:
IntVector v2( 100, 1234 ); // 100 个 1234.
IntVector v3( 100 ); // 100 个 0。
3) vector( vector const& other );
复制构造函数,复制 other 中的内容:
IntVector v4( v2 ); // 100 个 1234。
4) vector( const_iterator first, const_iterator last );
事实上,这个构造函数应该为
template&typename Iter&
&&& vector( Iter first, Iter last );
即拷贝任意的序列 [first,last) 到 vector 中。由于 VC++6sp0 编译程序的限制, Iter 被换为 const_iterator 了。
不过,碰巧 const_iterator就是 T const*,所以可以如下使用:
int a[] = { 1, 2, 3, 4, 5 };
IntVector v5( a, a + 5 ); // {1,2,3,4,5}
IntVector v6( v5.begin() + 2, v5.end() ); // {3,4,5}
5. 访问 vector&& 中的元素
以下成员函数/运算符用于访问 vector 中的一个元素:
T& at( size_t n );
T const& at( size_t n& const);
T& operator [] ( size_t n );
T const& operator [] ( size_t n& const);
T& front();
T const& front()
T& back();
T const& back()
请注意,由于 vector 是一个“值”语义的对象,所有的操作函数都必须严格保证 const 的正确性。所以,所有的元素访问方法都有 const 和非 const两个版本。
at(n) 和 operator [] (n) 都返回下标为 n 的元素的引用,他们的区别是,at() 进行下标越界检查,若发现越界,抛出 range_error 异常,operator[]不进行下标检查。
front() 返回下标为 0 的元素的引用,back() 返回最后一个元素的引用。
int a[] = { 4, 1, 4, 1, 5, 8 };
IntVector v( a, a + 6 );
// 使用 front(), back():
v.front() = 3;
v.back() = 9;
// 使用 operator [] ():
for( size_t i = 0; i & v.size(); ++i )
::std::cout && v[i] && 'n';
6. ::std::vector&& 的存储管理
以下成员函数用于存储管理:
void reserve( size_t n );
size_t capacity()
void resize( size_t n, T t=T() );
void clear();
size_t size()
bool empty() const { return size() == 0; }
size_t max_size()
另外,push_back(), insert() 等也涉及到存储管理,后面另行介绍。
1) max_size()
返回 vector&T& 理论上可以装的最多 T 的个数。这只是一个理论上的数字, 大概是 4GB/sizeof(T),没有多大实用价值。在程序中不要用。
返回 vector&T& 中实际装的 T 的个数。相当于 CArray&&::GetSize()。
3) empty()
如果 vector&T& 中没有任何 T 对象,返回 true。也就是返回 size() == 0。
4) clear();
清除 vector&T& 中的所有 T 对象。执行后 empty() 返回 true。大致相当于 resize(0),但不要求 T 可被缺省构造。相当于 CArray&&::RemoveAll()。
5) resize( size_t n, T t = T() );
将 vector 中的元素个数设置为 n,n 可以大于 size() 也可以小于 size。如果 n 小于 size(),那么 vector 中下标为 n..size()-1 的元素都将被解构。如果 n & size(),那么将在 vector 的后面新增加
n - size() 个相同的元素 t。在增大 vector 时,可能发生存储再次分配。总之,调用resize( n, t& 后,(size() == n) 成立。
请注意,如果调用 resize( n& 不带参数 t ,那么 T 必须可以缺省构造。
6) reserve( size_t n );
事先分配至少可以保存 n 个 T 对象的空间。调用后 (capacity() &= n)成立。
7) capacity();
返回已经分配的存储空间够容纳的 T 类型对象的个数。后续的增加元素操作(如 push_back(), insert())如果增加元素后 vector 中的总元素个数不超过 capacity(),那么 vector 的实现保证不重新分配存储空间。
vector 管理的动态存储空间是连续的。执行操作
IntVector v(7, 1); // seven ones.
v.reserve( 12 );
后,v 的状态可以用下图表示:
&/--size()---
|1|1|1|1|1|1|1|-|-|-|-|-|
&--capacity()---------/
其中,1 是已经构造的 int 类型的对象,- 是可以构造一个 int 类型的对象,但还没有构造的原始空间。再执行
v.push_back( 2 ;
v.push_back( 3 ;
后,v 的状态可用下图表示:
&/----size()-----
|1|1|1|1|1|1|1|2|3|-|-|-|
&----capacity()-------/
执行 resize( 11, 4 ; 后:
&/----size()---------
|1|1|1|1|1|1|1|2|3|4|4|-|
&----capacity()-------/
capacity() &= size() 总是成立的。对于下标为 [size()..capacity()-1]的未构造对象的存储空间,是不可以访问的:
v[11] = 5; // undefined behavior - anything can happen.
7. 添加元素到 vector 中
下列操作添加元素到 vector 中,并可能引起存储分配:
void push_back( T const&
void insert( iterator pos, T const& t=T() ;
void insert( iterator pos, size_t n, T const&
template&typename Iter&
&&& void insert( iterator pos, Iter first, I
push_back() 是把一个元素添加到 vector 的末尾。insert() 是把一个 t,或 n 个 t,或从 first 开始到 last 结束的一个序列插入到 pos 指示的位置之前。
当插入元素后 size() 将会大于 capacity() 时,将引起自动存储分配。vector 将会分配一个比需要的存储区大若干倍(通常是1.5到2)的新的存储区,把老的元素拷贝过去,同时完成添加或插入,然后释放老的存储区。
这就是说,vector 自动存储分配的空间大小是指数式增长的,这可以保证多次添加元素到 vector 中时,平均用时是接近于常数的。
// add 0, 1, ..., 99 to v:
for( int i = 0; i & 100; ++i
v.push_back(
// append 9, 8, 7,..., 0 to the end:
int a[] = { 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 };
v.insert( v.end(), a, a + 10 ;
8. 删除元素
下列成员函数完成元素删除:
void erase(
void erase( iterator first,
void pop_back();
void clear();
这些函数分别删除一个,一串,最后一个,或全部元素。
for( int i = 0; i & 100; ++i
&&& v.push_back(
// 删除 50, 51, ..., 89:
v.erase( v.begin() + 50, v.end() - 10 ;
// 删除 49, 48:
v.pop_back();
v.pop_back();
// 全部删除:
v.clear();
注意,删除操作不会引起存储分配,因此 capacity() 不变。
9. 作为序列访问 vector 中的元素
序列(sequence)在 STL 中是一个非常重要的概念,所有的容器类型和算法都涉及到,而且所有的算法都是建立在“序列”这个概念之上的。
“序列”是一个线性结构,由一个指示其起始和一个指示结束的叠代子(iterator)来决定。如果 first 和 last 是某种类型的叠代子,那么经常用[first, last) 来表示一个序列。注意,first 指向的元素是这个序列的一个元素,而 last 指示的是这个序列最后一个元素之后的位置,可能根本没有元素可以访问。这种半闭半开的区间表示是整个 C++ 标准中的约定,而且确实可以简化程序。
叠代子是传统的 C/C++ 中指针的抽象和进一步分类。在 C++ 中把 iterator划分为 input iterator, output iterator, forward iterator,bidirectional iterator, random access iterator 五类。其中的 randomaccess iterator 是最强的一类,即允许的操作最多。C++ 中的指针类型以及vector&&/deque&& 的 iterator/const_iterator/reverse_iterator/const_reverse_iterator
都满足 random access iterator 的要求。
vector&& 中定义了以下函数用于获取被控制(管理的)序列(动态数组)的各种叠代子:
iterator begin();
iterator end();
const_iterator begin()
const_iterator end()
reverse_iterator rbegin();
reverse_iterator rend();
const_reverse_iterator rbegin()
const_reverse_iterator rend()
这里我们不讨论叠代子的一般概念,只举几个 random access iterator 的例子:
int a[] = { 1, 2, 3, 4, 5, 6 };
[a, a + 6) 是一个随机访问序列,指示了 a[] 中的所有元素。这里叠代子的类型为 int*。
[a + 2, a + 4) 也是一个序列,指示了 a[] 中的 3, 4 两个元素。叠代子的类型仍然是 int*。
IntVector v( 100, 1 ; // 100 个 1。
[v.begin(), v.end()) 是一个随机访问序列,指示了 v 中的所有元素,叠代子的类型是 IntVector::iterator。
[v.begin() + 10, v.end() - 20& 也是一个随机访问序列,指的是 v 中除了头 10 个和尾 20 个元素外的其它元素。
[v.rbegin(), v.rend()& 是一个随机访问序列,指的是 v 中的所有元素,但与 [v.begin(), v.end()& 不同,这个序列是从尾到头遍历所有元素。
[v.rbegin() + 20, v.rend() - 10) 与 [v.begin() + 10, v.end() - 20 指示的元素相同,但遍历顺序相反。
下图是有十个元素的 vector 的 begin()/end()/rbegin()/end() 的示意:
begin() ----------& end()
& |&&&&&&&&&&&&&&&&&& |
& v&&&&&&&&&&&&&&&&&& v
&|0|1|2|3|4|5|6|7|8|9|
^&&&&&&&&&&&&&&&&&& ^
|&&&&&&&&&&&&&&&&&& |
rend() &---------- rbegin()
for( int i = 0; i & 10; ++i
v.push_back(
// print 0, 1, 2, ..., 9:
for( IntVector::iterator i = v.begin(); i != v.end(); ++i
::std::cout && *i && 'n';
// print 9, 8, ..., 0:
for( IntVector::reverse_iterator i = v.rbegin(); i != v.rend(); ++i
::std::cout && *i && 'n';
除了使用 begin()/end()/rbegin()/rend() 来遍历 vector 中的元素外,由于 vector 管理的空间是连续的,因此可以直接取地址进行处理:
::std::vector&HANDLE&
handles.push_back( handle1 ;
handles.push_back( handle2 ;
::WaitForMultipleObjects(handles.size(), &handles[0],TRUE, INFINITE);
这在与 C 库函数接口时尤其有用。
10. 赋值和交换
vector&& 是可以赋值的,这也是一般的“值”类型必须提供的操作:
IntVector v( 100, 123 ;
IntVector v1;
vector 另外还提供了
template&typename Iter&
void assign( Iter first, I
void assign( size_t n, T const& t = T() ;
用于赋值:
int a[] = { 1, 3, 5, 7 };
v.assign( a, a + 4 ; // v 将包含 1, 3, 5, 7.
v.assign( 100 ; // 100 个 0。
还有一个很重要的操作:
void swap( vector& v& throw();
用于交换两个同类型的 vector 的值。它的特点是快速(只需要交换内部的三个指针),不产生异常。这在写一些保证异常安全的程序时非常有用。
事实上,swap() 基本上已经被当作类似于 operator=() 的一个“值”类型应该提供的基本操作,::std::swap() 也应该为用户定义的类型进行特例化,调用相应的类的成员 swap() 函数:
struct MyVal
& // blah blah.
& void swap( MyVal&& throw();
namespace std {
& template&&
&&& void swap( MyVal& a, MyVal& b
&&& { a.swap( }
关于 swap(),值得专文讨论。这里我们只指出,vector&T&::swap() 是快速的,不抛出异常的,很有价值。
11. 使用 vector 时的存储管理策略
从前面的介绍中可以看到,vector 的自动存储分配是指数式的增加存储空间,而且永不缩小已经分配的空间。这在大多数情况下是合适的。如果应用程序事先知道要用到的元素个数,可以先调用 reserve() 来保留(分配)空间,这样可以避免以后增加元素时不必要的重新分配和元素拷贝:
v.reserve( 100 ;
for( int i = 0; i & 100; ++i
&&& v.push_back(
请注意,reserve() 和 resize() 是本质上完全不同的。reserve(n) 保留的是未使用而能够使用的原始空间,而 resize(n) 是真的创建了 n 个对象:
v.resize( 100 ; // v 已经包含 100 个 0.
for( int i = 0; i & 100; ++i
&&& v[i] = // 可以赋值
有时候,一个 vector 可能增长到较多个元素,然后又减少到较少的元素个数,这时,可能希望缩小 vector 分配的空间以节约内存。CArray&& 中提供了 FreeExtra(),但 vector&& 并没有提供相应的函数。这时必须进行复制:
IntVector(v).swap(
有一种看法认为拷贝构造函数同时也复制了capacity(),而标准中并没有很明确地指出这一点,因此更安全的方法是
IntVector(v.begin(),v.end()).swap(v);
如果一个 vector 中可能要存储的元素个数较多(例如,超过100个),而且事先无法确定其个数(因此无法调用 reserve()),那么通常 vector 不是一个恰当的数据结构,应该考虑用 ::std::deque&&。与 vector&& 相比,deque&&不保证背后的存储空间是连续的(因此象上面的WaitForMultipleObjects()中的应用不能用 deque&HANDLE& 代替),但有较好的伸缩性,还可以在数组的前端用 push_front()/pop_front() 增减元素(hence
its name, doubly endedqueue)。
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:109870次
积分:1620
积分:1620
排名:第11513名
原创:20篇
转载:224篇
(8)(6)(10)(41)(29)(10)(12)(23)(9)(13)(20)(30)(6)(3)(3)(8)(10)(4)

我要回帖

更多关于 jsp iterator 遍历 的文章

 

随机推荐