找回密码
 注册
搜索
查看: 5661|回复: 0

使用::std::vector<>作为管理动态数组的优先选择

[复制链接]
发表于 2008-2-26 13:29:18 | 显示全部楼层 |阅读模式
作者:wangtianxing
; Z- s' m  t$ ^8 _8 q7 D
+ k4 M& Y, t+ \原文出处:http://www.cpphelp.net/issue/vector.html1 l/ F8 c# q3 p* Q5 U! A4 ]8 s

' V. @1 E! m% e& i
( ~" H1 B0 q! [, ]+ G! ]& c8 ]3 ~; b! ^2 D5 X0 c+ u
摘要: 本文介绍了C++标准库中的容器类vector,分析了它的优点,并且建议在应用程序中使用它作为动态数组的优先选择,而不是MFC的CArray<>等其他类模板。最后介绍了vector的接口和使用时的注意事项。  r) e) a- k; r: l! F$ z4 P
/ H) G( j) q% _# V
在一些使用 MFC 的程序中,经常看到许多程序使用 CArray<>,由于 CArray<>的设计问题,造成使用它的代码的复杂化,增加了维护难度。因此建议使用 ::std::vector<> 代替 CArray<>。
; P  Z  @8 T( p, b' b: ?3 B' J
% K2 W1 ?( `6 ^% c另外,也看到一些程序在用 malloc/realloc/free/new[]/delete[] 等手工管理内存。在应用程序中,手工管理内存是容易导致错误的,应该用 ::std::vector<> 之类的对象来管理动态数组。
# x% M0 x' ~! y, E5 Y' G2 X2 D
+ v3 z. o) h2 i0 I2 s0 Z. n由于 MSDN 中关于 ::std::vector 的内容较少,我们在这里做一些介绍,供参考。; B, l. `" ]  p; C: W" C% `! K1 w
2 h: V) F* a" e6 \) |0 [
不熟悉 CArray<>/WIN32 也没关系,这里提到它们的地方并不太多。; j8 x# w8 t" D3 n3 V' E

  S9 V) k/ X: _+ M- N1. CArray<> VS ::std::vector<> ?
! D1 L" G3 N2 qCArray<> 和 ::std::vector<> 一样,都是模板类,用于管理任意类型的对象的动态数组。都在解构时释放所管理的动态内存。因此都可以用于代替手工动态数组管理。$ s6 l# c7 p- X5 D7 E4 X

  N* h+ g  V; g0 o' q3 T! E但是,CArray<> 是在 C++ 标准化之前很多年(VC++2.0时代)设计的,当时对 C++程序设计,面向对象程序设计,模板程序设计等技术认识严重不足,尤其是当时对面向对象技术的错误信仰与宣传,造成 CArray<> 的设计有重大错误。
  h6 [: h3 e' [# }
0 B2 W# D, Z$ ~2 P( s在 C++ 语言标准化以后(1998),以及 VC++ 6.0 出世以后,提供了标准的::std::vector<> 模板,基本上在任何方面都要优于 CArray<>。Microsoft 由于要支持老的程序,因此一直保留了 CArray<>,但显然并没有打算按照新的思想去发展它(至少应该提供operator=(CArray const&)吧)。/ H4 P1 h6 s3 h

* ~9 L6 _$ M  F$ ]概括起来,CArray<> 与 ::std::vector<> 有以下不同:5 |# D  A$ v1 _, N, d7 p

2 g0 o  k1 ^& k0 @( ~* a" D/ q1) 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<> 没有继承任何东西,只是实现了管理一个动态数组该做的事。8 Q/ ], Y0 D+ N
% s6 {& O9 }, [+ o# m
2) CArray<> 不是一个恰当的值类型,例如下列操作都是不合法的:& }( {$ V, g) Z2 z# C

# S6 b' u: |# i; yCArray<int,int> a;
& a+ b; S( W: _; w1 S4 U/ `CArray<int,int> b(a);  // error, must use Copy().
- |. B9 J  R' u" K( x5 nb = a;        // error, must use Copy().& Z7 h9 ^: e! b
b == a;       // error, you must write your own.$ a+ ]+ y/ V( S9 Q* ~* p1 M
b < a;        // error, you must write your own.
4 k; k1 V7 o1 }与 CArray<> 相反,::std::vector<> 是一个认真设计的值类型,天生是可以拷贝构造和可赋值的。如果 T 是可比较的,那么 ::std::vector<T> 将自动地是可以比较的。
/ |+ f4 n$ {1 z2 O5 r. ~" W# h& S( e7 c' m1 x, f7 @
此外,由于涉及到四个特殊成员函数;
+ y3 U( }! z) {2 p) S+ @' k+ U! _) ~: k: O- z( V4 l" S
T(); // 缺省构造函数(default constructor)- }0 ]# [6 r( H$ L0 g# n1 r) t; x
~T(); // 解构函数(destructor)* S+ t% B9 j2 j+ g* m( {: W3 l& r6 o
T( T const& ); // 拷贝构造函数
% M; h3 c7 o" A# c5 l$ n! A0 LT& operator=( T const& ); // 拷贝赋值函数
% x* i$ D% a1 y% D, ?的自动生成,如果使用 CArray() 作为 T 的成员变量,那么上述的四个特殊函数中的后两个将无法自动生成,需要手工写:/ A" x# D  |, b8 ~( D7 j

  u: s6 S' y2 [# V struct T$ c& b: Q6 H* p0 s, a
{) H+ T! T; [% F6 ]! H8 ~
   T() {}
! s6 D  ?+ j: e   T( T const& t )
) `, [4 @/ o! H5 T8 N' P   {% O5 e" U8 @' R) X. v5 ~& p: y1 m
       a_.Copy( t.a_ );7 M5 c/ D9 F3 v
       i_ = t.i_;
7 @0 [( p9 O: ~0 c( x       d_ = t.d_;% g- V. g2 c( A- ?" ?4 ]
       s_ = t.s_;
! Z/ i& [$ A/ P3 C7 A1 X! o: n- P   }3 k  V. [* K+ M  \* f
   T& operator = ( T const& t )
5 u6 [1 n6 a: c: H1 h# O; [6 g   {
. x( n# f8 q& K/ |+ m       if( this != &t )
) h% W* s8 A! A       {
7 z3 U2 f6 Y$ D           a_.Copy( t.a_ );2 h' c1 e- ^; l9 r
           i_ = t.i_;
/ y7 }2 [. Q7 u# f, p0 O           d_ = t.d_;6 L& D( N  }9 I3 Y! m
           s_ = t.s_;; ^& v9 j" t7 U+ G2 v: J
       }, R6 n; ~% Y: r9 ], y3 t, O
       return *this;) y5 ]9 c  u) @7 k2 ^
   }1 o4 b2 T6 i( l2 j" Q- D9 }
private:1 j" f5 V: t4 U6 G( K: E  ~
   CArray<int,int> a_;
* w& }" ]" [5 l6 ?, t) }3 t   int i_;
! L; }/ ~5 ?* ~& N4 o   double d_;7 d, r* i: k+ G* A1 b
   ::std::string s_;+ J. J+ Q9 D/ |2 E: `  u* G
};: x2 e4 F3 T; O1 v2 F
如果使用 ::std::vector<>:) |( M( A) @0 j* S7 [, [

8 ~" Y0 x6 I5 ^: C# Fstruct T+ r7 @/ |% A: a6 @- Y' a& ]" n
{
# ]% g/ P) N- T# v2 [, }private:) W+ V. {* `- b. B$ j
   ::std::vector<int> a_;
0 Q+ S" ~6 c) h+ O/ Z; r" r   int i_;! A2 S3 t; }1 F1 m1 E
   double d_;; q3 z) R& Z; X. @9 t- c% C/ W
   ::std::string s_;! d; D& b% I9 {/ ^2 m6 p, j) S
};
2 T& `- f! G9 R( y) V( a; m上面列出的三个特殊成员函数都不需要写。好处是明显的:当你增减 T 的成员变量时,你不必到
5 d# }; E; C% j/ J( i2 KT(T const&) 和 operator=() 中去相应地增减。
" F, r' o7 Q/ \% R0 O5 ^( ]! ~
- g/ G- G% H. h( h- @8 c. A; Z3) 没有现成的算法可以对 CArray<> 进行操作,而标准 C++ 里的标准算法大多都可以直接在
5 X2 l8 ]( e8 d( l::std::vector<> 上运行。例如:- x! ^" t5 _  h7 A

2 \0 f" k+ \4 f( ostatic int const init_vals[] = { 3, 1, 4, 1, 6, 9 };
2 ]) R, U1 n; G, R4 H  |3 k1 Mvector<int> a( init_vals, init_vals + 6 );: ~$ k7 \' s# K/ N9 c- Y, V
*find( a.begin(), a.end(), 6 ) = 5;    // 把6改成5
# x1 @) x0 R- d6 Y3 E* Zsort( a.begin(), a.end() );    // 排序。
' W( m0 M) {7 l- b7 y' n4 z$ \0 D) [可以说,CArray<> 的主要设计错误是把一个本来应该是一个简单的“值”类型的东西设计成一个难用的“对象”类型了。所有的“值”的好特性都丧失了,但那些从CArray<>继承的派生类呢?
6 d  _1 \% D* ^! @; ~. V' m' \2 U/ v) t
CByteArray等的问题与 CArray<> 的问题一样,甚至更多(例如,CPtrArray,永远不要用)。
" e; x+ L* s! [! w5 A. f$ c" W* w- \6 E- G* U
同样,其他的 MFC container 模板,象 CMap<>, CList<> 等,都有类似问题,都应该用
, w( x3 F$ w. x4 y::std::map<>,::std::list<> 等设计更好的东西代替。/ [; a4 o' i7 O/ M7 \

1 T" i! g% @. d- G  O4 P2. ::std::vector<> 在哪里?$ H8 V" ~" `( t( v3 z
::std::vector<> 在头文件 <vector> 中定义:0 d: C, j# g1 f  y8 @3 W" y6 j) j
$ {6 `) z+ h( \5 b) \% |
(注意,标准的 C++ 头文件都没有 .h 后缀,有 .h 的文件是与 C 兼容的,或支持老的不标准的东西,象 <iostream.h>。)( j$ u: P, c/ w( {) x8 Z
* K- v. f! e* l' f( t. q
namespace std ! X$ ~! U9 U2 A) y
{1 ^8 l4 m+ H5 _+ T% g
    template<typename T, typename A = allocator<T> >" H  u$ W) Q! [
    struct vector
( B0 i; k8 H9 H    {+ ?- @" W7 U2 Q; L% u; ?
        // 具体内容稍后讨论' @0 u& n, @9 p* `: g# A
    };
8 t2 K6 R2 `2 t6 ]6 |( t: v3 T& E4 h
7 l$ D5 U4 K0 e" R/ u/ s0 @
    template<typename T, typename A>; h' H) c/ S0 h$ q& J! y
        bool operator == ( vector<T,A> const& a, vector<T,A> const&    b );5 F4 q) `! `. K4 K
    template<typename T, typename A>! \1 @, b! H/ E9 v6 X; e
        bool operator != ( vector<T,A> const& a, vector<T,A> const&    b );" H: F$ x+ d/ ?: i2 x1 a/ M( T
    template<typename T, typename A>7 j% @7 V$ L& {- ]3 w' K1 p
        bool operator < ( vector<T,A> const& a, vector<T,A> const&    b );
. r7 N( J9 }! m3 Y8 f8 }9 B1 u. ^9 ?6 N    template<typename T, typename A>
( f0 v$ }2 b" v6 `$ h3 {        bool operator >= ( vector<T,A> const& a, vector<T,A> const&    b );: W: J+ L% z; |( C3 J5 X5 o
    template<typename T, typename A>
, Q, ^) o* c4 C; E        bool operator > ( vector<T,A> const& a, vector<T,A> const&    b );
* h6 P3 ^: s) v" q$ h( A5 \, R    template<typename T, typename A>
( ?- c! \/ }+ z        bool operator >= ( vector<T,A> const& a, vector<T,A> const&    b );$ z3 i1 D' |* S; k
}
! a+ @  ^, ^& w& ^: evector<> 定义在 namespace std 中,使用时为了减少击键次数,通常使用一个类型定义缩短类型名称:
) x0 v5 Y& g7 p. E( c) B8 K! `# X3 n0 x, m2 z! E, T
#include <vector>4 H7 c4 U; z$ u9 {  |; b
typedef ::std::vector<int> IntVector;
  r# _6 }7 X9 f. `IntVector a;+ v0 G  h2 Q7 p4 G
IntVector b( a );
" j0 N: J7 u9 ^IntVector c;+ Q; v. F, ^) {6 |$ G& ]% S
c = b;- p; i5 g: G' w: e: E' Y$ \
assert( a == c );
) b) }  p" {; F请注意 <vector> 中定义了六个 vector<T,A> 的比较函数。这些函数只在真的用到时才会被实例化,才会要求 T 也提供 operator==() 和 operator<()。6 G) X& |1 k( n/ F* V3 C* a: A

& G2 f! z  N1 T" e- R* z1 L) y另外,A = alloctor<T>:用于提供一个用户定义的存储管理类。由于这个参数很少用到,而且在 VC++6 的实现中有问题,不能用,因此以下的讨论忽略这一部分的内容。7 [- [6 ?1 N+ Y8 z# w8 ~0 K
  }* Z% A% A4 x) j2 o/ B$ L
3. ::std::vector<> 中的类型定义
; j, c  ?% k6 ^) e4 Z" b+ p" gvector<> 中定义了一些类型,下面只列出常用的:
- n, S+ T' F/ `4 K
5 f# V- F/ g* ctypedef T value_type;( I3 J7 _7 H, d6 S6 |
typedef T0 iterator;" {1 D2 w- Z$ E8 W
typedef T1 const_iterator;7 g. h2 Y" y& S) V  u+ L
typedef T2 reverse_iterator;
; G7 @* n9 |2 s; X, b2 [+ gtypedef T3 const_reverse_iterator;
2 r5 {" A& s% a7 O& W/ B, [3 a+ Y* l8 ]8 G6 ?
value_type 就是 vector<T> 的元素类型,也就是 T。当写通用的算法处理任意类型的 vector<> 或其他容器类型时是很有用的。1 a1 f' o3 L- z6 g4 ~) c6 D) p- i

2 Z: ?) Y6 v3 C* E9 xiterator/const_iterator 是两个 vector<> 的实现定义的未知类型,用于访问vector<> 中的元素,类似于 T*/T const* 指针,他们的区别是一个指向的元素可被修改,另一个只可以读:
" M. i  E  J: N) |; N- l8 l( M
, W' H: q3 O) r$ Ktypedef ::std::vector<int> IntVector;
3 E7 y# L  W3 \, p6 s  l0 ?IntVector::iterator iter;
6 u& M0 d$ K# q) n2 @6 ?& AIntVector::const_iterator c_iter;* s# ~' d3 _6 o. t* J
// ...3 i8 b7 ~, [  u* M
++iter; iter++; // ok: increment, post-increment.( ]4 ?  J& X( N$ m: [4 w' S
--iter; iter--; // ok: decrement, post-decrement.7 A3 M2 G8 `- c2 }! a' y
++c_iter; c_iter++; // ok: increment, post-increment.. t/ A; @( D' W' M0 J3 b6 k1 E
--c_iter; c_iter--; // ok: decrement, post-decrement.) h8 _. Z5 ?% |6 G( m# I
*iter = 123; // ok.
2 `6 C% R4 y( [int k = *iter; // ok.
0 d8 g9 @- o# N$ Ek = *--c_iter; // ok.
6 }+ I$ V$ q- L+ Z+ F*c_iter = k; // error.
; S: ^7 X' P" J! W: y/ zc_iter = iter; // ok: iterator is convertible to const_iterator.* m1 N* [" b2 p
iter = c_iter; // error: can't convert const_iterator to iterator.
2 k+ g* d1 U5 J0 d6 G2 {) U在使用上 iterator/const_iterator 和 T*/T const* 基本相同,事实上有些vector<> 的实现里就是用 T*/T const* 实现 iterator/const_iterator 的,但又不可以把 iterator/const_iterator 当作真正的 T*/T const*:
% r& S3 v0 Q2 g: a/ C# o0 T8 X/ n! M& E, e: q
T* p = iter; // may fail to compile.! s& \9 x1 l8 `
T const* q = c_iter; // may fail to compile.
+ B( z( }2 O# z8 g; T1 m! a* X. areverse_iterator/const_reverse_iterator 与 iterator/const_iterator 类似,但以相反的次序(从尾至头)访问 vector 中的元素。7 u7 A  {3 W, ?  p( a
7 B" N7 |( D4 D3 M( z! n
各种各样的 iterator 在 STL 中有特别重要的意义,但这里我们不做具体介绍。只要理解通过 iterator 可以访问 vector 中的元素,大概相当于一个指示位置的指针就行了。
. F% O  _% J6 c) X$ E8 z6 m7 y) j9 }5 o
4. ::std::vector<> 的构造3 R& }; p; G/ J
vector<> 提供了以下构造函数:(忽略 allocator 参数)+ p. _$ ~+ {7 C0 W

! F+ e. Y- G4 dvector();2 v/ u' W) u; ~) X( B. @/ v
vector( size_t n, T const t=T() );
; N3 c* F2 n! n$ m$ \" s! `) @& svector( vector const & );
+ ^2 y" ~# D, {" I' }9 [) d5 mvector( const_iterator first, const_iterator last );6 h' ]% F1 [+ Y' c2 ~. }
1) vector();0 T# k2 n# i& [7 i% T: ?
" U' J" D0 s, h6 o4 j
构造一个空的 vector,不包含任何元素。
* c8 y6 y) A$ Y3 D+ o& f3 b! d7 D
7 I% C0 c4 M. k+ d* j6 mIntVector v1; // 空的整数向量。
0 ?6 M% D$ p# w' t) `3 E( ]2 G7 `- m2) vector( size_t n, T const t=T() );% k) j4 v/ f% _) z$ b  ~" d

" J4 k# G+ w) q2 F3 d/ y. V: m# |构造一个 n 个相同元素 t 组成的 vector。如果不给出 t,那么将用 T() 做缺省值:
7 M- C) S$ K2 a9 }$ h! x: H
. ^5 ^7 x/ [1 c" s# Y! Y: g6 UIntVector v2( 100, 1234 ); // 100 个 1234.
/ }  o9 T7 z4 p. q9 bIntVector v3( 100 ); // 100 个 0。
* A) P, H: f2 }8 p% v$ z5 [3) vector( vector const& other );; M7 b+ m) E4 [2 \& M
/ B' ~' I8 ?. K# G
复制构造函数,复制 other 中的内容:
- l7 t0 h4 j& b0 [0 t& {! T- F+ u( p3 u5 H
IntVector v4( v2 ); // 100 个 1234。8 D% G" u+ @0 x5 v3 p9 i5 G) x
4) vector( const_iterator first, const_iterator last );
0 Z0 R7 g7 X# E2 N' M4 C' z1 J# R/ j
事实上,这个构造函数应该为
1 o1 E& ]# X5 h7 {, M9 D9 E2 Y3 p* L6 M4 v9 O
template<typename Iter>
$ q! O; z3 I0 t! Z    vector( Iter first, Iter last );& W8 @$ ~% y% [1 P* S+ t! _1 G
即拷贝任意的序列 [first,last) 到 vector 中。由于 VC++6sp0 编译程序的限制, Iter 被换为 const_iterator 了。不过,碰巧 const_iterator就是 T const*,所以可以如下使用:9 L7 i: `6 e! Y. b& ]
2 `# {! z3 t0 ^# a6 ~3 K& [1 v0 d. u
int a[] = { 1, 2, 3, 4, 5 };
: c* I* x$ y. IIntVector v5( a, a + 5 ); // {1,2,3,4,5}
) d! |" T, O+ Z) XIntVector v6( v5.begin() + 2, v5.end() ); // {3,4,5}
, c3 [! g$ L/ {2 n% K6 Q5. 访问 vector<> 中的元素
5 J  ?5 x; v! V  W以下成员函数/运算符用于访问 vector 中的一个元素:6 e! z* _0 u! U3 D, L5 }
# ^& S6 d" t) @
T& at( size_t n );
) |) ]9 \/ h3 }( y3 f# G  XT const& at( size_t n ) const;! U3 D; m* c, I, p* b! P
T& operator [] ( size_t n );. x8 ^+ I- p3 `0 }
T const& operator [] ( size_t n ) const;6 j/ L( k% k+ a+ _/ a. `. K" g- D
T& front();$ O, p# D  z9 ^# F1 h9 H  ~
T const& front() const;# x' P/ H3 {3 d7 A0 n9 m3 M% T. E
T& back();
4 i1 H; n% p6 k0 WT const& back() const;/ u3 [2 E+ I0 m
请注意,由于 vector 是一个“值”语义的对象,所有的操作函数都必须严格保证 const 的正确性。所以,所有的元素访问方法都有 const 和非 const两个版本。
: M( O: a- |0 b$ ]& n  i
  Z, K4 n& G. ]+ P7 o5 ?' D" z  ~at(n) 和 operator [] (n) 都返回下标为 n 的元素的引用,他们的区别是,at() 进行下标越界检查,若发现越界,抛出 range_error 异常,operator[]不进行下标检查。
, y) y  {' W  ~$ V& m% j% w. P3 Z) c; i0 t' o2 `$ a
front() 返回下标为 0 的元素的引用,back() 返回最后一个元素的引用。) U$ j3 z2 K$ c! x, r9 W

+ b4 @' J& a2 `& |5 m  uint a[] = { 4, 1, 4, 1, 5, 8 };5 l8 E+ B/ L" S
IntVector v( a, a + 6 );8 U) D' e3 _: X& x' h
// 使用 front(), back():
  C. i' Y$ M* H8 A2 x  Cv.front() = 3;7 A5 Y$ n6 Q" D) s* O: G9 K
v.back() = 9;
: V8 Y1 `8 x2 K9 g7 n// 使用 operator [] ():
+ t/ [( U. E1 @( d' Kfor( size_t i = 0; i < v.size(); ++i )5 r- C0 }" P6 _. W5 q
::std::cout << v[i] << '\n';8 y* ~8 C5 b% f; `* y
6. ::std::vector<> 的存储管理1 D: J! U, b. ?  l1 Z% a
以下成员函数用于存储管理:1 p% D4 z+ u& u
2 g7 C- K  x+ q' ?7 `
void reserve( size_t n );* `) ?3 X7 p4 P) p. Z
size_t capacity() const;+ u1 v) X+ o0 n
void resize( size_t n, T t=T() );. ~- n+ s) z, i& y# N: }
void clear();
2 T" `0 z0 `$ Z: {size_t size() const;
6 ^$ z4 k( X- ~' x! ?. B2 r8 Rbool empty() const { return size() == 0; }
4 S# e5 }( `+ b/ xsize_t max_size() const;
* ?7 S* U" t$ z4 x$ q
/ W& K" m4 g& X/ Z' N. p另外,push_back(), insert() 等也涉及到存储管理,后面另行介绍。2 K! [* S/ W* H, f+ O6 I+ f
+ r9 j9 E+ z3 ]  E# N2 f: @; n
1) max_size()
/ n% z0 b$ A2 V! c: \; u/ q, j! p* g3 `( _% w9 t" {) h
返回 vector<T> 理论上可以装的最多 T 的个数。这只是一个理论上的数字, 大概是 4GB/sizeof(T),没有多大实用价值。在程序中不要用。0 U. F% {& y" K4 e1 E$ g
2 \1 O, P7 x! ~7 e4 m( N$ ^% S8 x
2) size()
" v2 h. m" Z6 ]5 k  ^9 h5 W1 f# ~: X( _' K" X, |
返回 vector<T> 中实际装的 T 的个数。相当于 CArray<>::GetSize()。
, \/ U: U: ^4 }2 Y( f+ D& |) M
$ A, i: Q/ m3 ?. G  K5 n2 O* G3) empty()
4 Q6 H' y7 V) o
5 ]" }! J! s. Z  G( x如果 vector<T> 中没有任何 T 对象,返回 true。也就是返回 size() == 0。3 j3 q/ X) c" k7 c& ~

, s; Y# I. C  i) U( a" m, [6 g4) clear();
2 v3 G" W% N! ~0 T1 R# s0 n6 Z' ^$ p7 ~/ Q8 G( o* g
清除 vector<T> 中的所有 T 对象。执行后 empty() 返回 true。大致相当于 resize(0),但不要求 T 可被缺省构造。相当于 CArray<>::RemoveAll()。
/ Z( U" `3 k+ m& ~$ k5 V: {  J' h2 p4 p3 t
5) resize( size_t n, T t = T() );- B& }! ~4 l1 D% e

: Y; r) |  \3 ~  n将 vector 中的元素个数设置为 n,n 可以大于 size() 也可以小于 size。如果 n 小于 size(),那么 vector 中下标为 n..size()-1 的元素都将被解构。如果 n > size(),那么将在 vector 的后面新增加
7 P. I5 U- H/ ^" K2 Un - size() 个相同的元素 t。在增大 vector 时,可能发生存储再次分配。总之,调用resize( n, t ) 后,(size() == n) 成立。
' G6 ^& y" G7 R$ v8 Y* w1 W
. I! t8 t7 D, J请注意,如果调用 resize( n ) 不带参数 t ,那么 T 必须可以缺省构造。+ [) t; [' c/ }/ W" I

. ~( {: _* i1 f; L! y/ w6) reserve( size_t n );
6 y0 B$ C1 B; ?" A: z  v2 ?' a  A) x
事先分配至少可以保存 n 个 T 对象的空间。调用后 (capacity() >= n)成立。
$ @! M; e$ c1 \" z( ?4 ~1 T  J* B( v/ x* f1 }; d1 C* I
7) capacity();7 q2 C: M. W" T' f3 ?
2 N' _7 s" a2 K- J  u
返回已经分配的存储空间够容纳的 T 类型对象的个数。后续的增加元素操作(如 push_back(), insert())如果增加元素后 vector 中的总元素个数不超过 capacity(),那么 vector 的实现保证不重新分配存储空间。
' x: E6 L+ d9 h' y
  V' R7 Q5 w! T$ j- {# |/ O* Bvector 管理的动态存储空间是连续的。执行操作
8 U5 C, B. \8 ?! U# e
3 D" ?' O1 h' D  T! S9 i: zIntVector v(7, 1); // seven ones.5 v6 w3 d5 E( N9 v8 |& M; y0 `
v.reserve( 12 );
- h( F0 I) O& W6 R: a  _* M后,v 的状态可以用下图表示:/ s$ [3 M* |7 X# I$ j9 J

, m2 w* I7 v$ l /--size()---\3 r+ N* ?; j- i5 R
|1|1|1|1|1|1|1|-|-|-|-|-|
5 F4 C+ D1 \; j: a \--capacity()---------/% h0 S4 w/ j3 @8 S, v3 N
其中,1 是已经构造的 int 类型的对象,- 是可以构造一个 int 类型的对象,但还没有构造的原始空间。再执行
. Y. X4 {  h! R/ ?& P1 v3 u6 G7 }. O  D; s9 K
v.push_back( 2 );
) l+ l! [" C% X/ W9 u& t; G5 D- @v.push_back( 3 );
7 J1 \: {) N/ m后,v 的状态可用下图表示:* Z- j# W3 X& p

; G- A% d2 t7 f" E/ V6 ] /----size()-----\
% `2 R! l/ k0 L|1|1|1|1|1|1|1|2|3|-|-|-|
' p  a* N5 |& Z" P) `6 x' D \----capacity()-------/
5 d4 x( N+ C: Z" `: T  H4 D2 _执行 resize( 11, 4 ); 后:
* ]& d6 k7 n$ u0 \% S
: F2 B% M9 c9 \& T9 r% [ /----size()---------\
* L: |# }; N. m|1|1|1|1|1|1|1|2|3|4|4|-|
* u* x7 Q3 v+ E! U* @) s \----capacity()-------/
6 ]$ ]% C1 M$ I* T9 Bcapacity() >= size() 总是成立的。对于下标为 [size()..capacity()-1]的未构造对象的存储空间,是不可以访问的:
* \0 |! B& K  Q4 `1 [" j. X; B
9 i/ I4 \5 ^3 B7 `6 h) ]v[11] = 5; // undefined behavior - anything can happen.3 t! \  w! d/ x0 h
7. 添加元素到 vector 中
  ]9 g/ F3 I+ T- d0 W6 d下列操作添加元素到 vector 中,并可能引起存储分配:
7 @8 ?9 N/ n" x" ?$ e# J
* i& a( R0 j. [  rvoid push_back( T const& t );
' L( W9 N4 G7 |0 p& o5 ]* [  m- }  Vvoid insert( iterator pos, T const& t=T() );
+ J; S2 u* I$ @; kvoid insert( iterator pos, size_t n, T const& t );
+ f8 ^! J/ S+ A! ^9 C. utemplate<typename Iter>
- W2 u& R6 p9 @7 @    void insert( iterator pos, Iter first, Iter last );
! x$ ]7 c( Q/ D9 l" ~2 jpush_back() 是把一个元素添加到 vector 的末尾。insert() 是把一个 t,或 n 个 t,或从 first 开始到 last 结束的一个序列插入到 pos 指示的位置之前。
& L& p+ r$ E4 j, {$ U+ M0 j6 C8 F+ a  T
当插入元素后 size() 将会大于 capacity() 时,将引起自动存储分配。vector 将会分配一个比需要的存储区大若干倍(通常是1.5到2)的新的存储区,把老的元素拷贝过去,同时完成添加或插入,然后释放老的存储区。
; ?) U. [- Z( ^; a! R
( f" n0 F! `7 u0 b. }; m3 G& x这就是说,vector 自动存储分配的空间大小是指数式增长的,这可以保证多次添加元素到 vector 中时,平均用时是接近于常数的。
- E3 Q9 ]; A  E/ }2 i  `# x
: Z$ G0 G$ M8 t( ]IntVector v;
5 }+ q* p: T1 {! P5 i  g1 L   
2 [# v* W& ]) C( v  s$ p5 ]( s// add 0, 1, ..., 99 to v:
; X$ o. C2 `; `+ ]1 y7 Kfor( int i = 0; i < 100; ++i )
' ]; c5 \/ }; ^v.push_back( i );
2 f( \' |/ F+ f9 ]   
- i) s! l: J6 S" B# x# V// append 9, 8, 7,..., 0 to the end:
* P) _9 P1 M  F  [int a[] = { 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 };" g/ t: e4 k! f7 W
v.insert( v.end(), a, a + 10 );
# t7 S# c* V! _7 f8. 删除元素  P9 l! x0 t7 @$ W  S9 M
下列成员函数完成元素删除:6 j$ z) D7 o+ H! W; d; V

, ]2 f& a. m% i, E! @. S5 F- p) [void erase( iterator );
* b7 O! a( Z; E! ivoid erase( iterator first, iterator last );7 ]% H; B9 ~6 x
void pop_back();
8 L# G7 V: j/ z1 O* P7 Ovoid clear();& f" `) G2 s  Q* b0 ?
这些函数分别删除一个,一串,最后一个,或全部元素。2 ^1 d. j1 X; i" M6 i7 B) l

& }  [7 y2 R; U5 y$ a" Q5 l# H! NIntVector v;$ G2 B; k* }8 ~
for( int i = 0; i < 100; ++i )
. o) i) r; t/ u" W1 }7 `    v.push_back( i );
- ~0 k  x, d& T     j! f  `& T9 i
// 删除 50, 51, ..., 89:, ]: f. K: L9 x1 Z, y5 X8 C
v.erase( v.begin() + 50, v.end() - 10 );
$ S/ k  w/ C2 t0 Q8 E   
  u( ^( _: C/ ?; l  F// 删除 49, 48:
, u7 J( `$ M9 P7 `3 E* u9 Iv.pop_back();" n# C1 U  H, y
v.pop_back();; M9 L& s9 Z8 j- h1 f
   & {- B/ |" c- Z' T1 n, W. Y: x
// 全部删除:+ \) w2 X" n2 u: E  V1 Y! e/ m' O
v.clear();' G( a8 a" x& L! }* K1 T$ `2 `1 S+ R
注意,删除操作不会引起存储分配,因此 capacity() 不变。
  O% l+ h5 L% }, B* a8 r6 Y5 e
3 e% S. f5 D5 r$ y9. 作为序列访问 vector 中的元素" i4 w6 C" e0 }0 k" H5 y- \/ C6 s
序列(sequence)在 STL 中是一个非常重要的概念,所有的容器类型和算法都涉及到,而且所有的算法都是建立在“序列”这个概念之上的。
# _% z* L4 D. q9 T1 r, w. I' U1 L
" s+ ?( u9 B/ `' M+ l& C% X“序列”是一个线性结构,由一个指示其起始和一个指示结束的叠代子(iterator)来决定。如果 first 和 last 是某种类型的叠代子,那么经常用[first, last) 来表示一个序列。注意,first 指向的元素是这个序列的一个元素,而 last 指示的是这个序列最后一个元素之后的位置,可能根本没有元素可以访问。这种半闭半开的区间表示是整个 C++ 标准中的约定,而且确实可以简化程序。* V- p4 h" V7 q8 S! W, i
: I, w9 \4 M) M: f% Y/ R6 }
叠代子是传统的 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 的要求。7 Y* w' g! ?' Z5 d
; {6 P2 O2 G8 l
vector<> 中定义了以下函数用于获取被控制(管理的)序列(动态数组)的各种叠代子:  |- q) ]/ O4 ~1 _# ^. P3 A
0 m/ `6 s- m) M8 r4 U
iterator begin();
! w8 Z* z% T/ B* S. eiterator end();# Q5 }- l8 ]/ @& ]2 Y
const_iterator begin() const;4 E2 c* B! i9 B6 u
const_iterator end() const;
" D- r; M9 ?$ E" ~5 U/ z/ ~reverse_iterator rbegin();1 g) G4 }3 I' e3 D* |
reverse_iterator rend();1 y6 [* O0 u; X0 @& i! U2 ]; C
const_reverse_iterator rbegin() const;- g9 T. ?' A0 H1 x
const_reverse_iterator rend() const;
( {+ h& \7 q4 e; D, d这里我们不讨论叠代子的一般概念,只举几个 random access iterator 的例子:
5 e2 M/ h) B6 j& C
* c# |/ V1 J4 @) U  S& mint a[] = { 1, 2, 3, 4, 5, 6 };
: Q5 l( `( U  t2 y. b7 i4 c8 k[a, a + 6) 是一个随机访问序列,指示了 a[] 中的所有元素。这里叠代子的类型为 int*。
( F: D5 E  ]7 b( b! U; J
2 l! ~( b$ g! Z0 t+ G; o/ D3 i[a + 2, a + 4) 也是一个序列,指示了 a[] 中的 3, 4 两个元素。叠代子的类型仍然是 int*。
" r. j3 k7 Q( [
/ u7 x0 L! e' T, k3 \- _/ c# ?IntVector v( 100, 1 ); // 100 个 1。
) b0 x! F" G# l9 w6 e3 i/ I1 q$ X[v.begin(), v.end()) 是一个随机访问序列,指示了 v 中的所有元素,叠代子的类型是 IntVector::iterator。: D3 u& M! ?) |7 B' Z' n  V6 f9 U# n$ J

- r: Z" [5 W" h[v.begin() + 10, v.end() - 20 ) 也是一个随机访问序列,指的是 v 中除了头 10 个和尾 20 个元素外的其它元素。$ q" G9 u' w0 k+ _3 D

* E& m5 T. `9 V) ~  e: @* x/ r[v.rbegin(), v.rend() ) 是一个随机访问序列,指的是 v 中的所有元素,但与 [v.begin(), v.end() ) 不同,这个序列是从尾到头遍历所有元素。/ }. k5 }9 X: H

2 k2 A2 _' G) M  s. `$ p. n[v.rbegin() + 20, v.rend() - 10) 与 [v.begin() + 10, v.end() - 20 )指示的元素相同,但遍历顺序相反。- h# }1 P  ^. W& d$ x
/ F) g) X' t* C/ o1 o8 @
下图是有十个元素的 vector 的 begin()/end()/rbegin()/end() 的示意:5 q8 H, C: h& r5 G5 z6 J; y# R' D- X; u
/ I; i% [$ [1 u5 O5 Q
begin() ----------> end()- U/ x" J! b$ s! ~
  |                   |
8 s, Q* O( g% o, H: {5 o) O  v                   v
7 I8 I2 W+ F' R$ z- ]% _ |0|1|2|3|4|5|6|7|8|9|
: |! c# x$ s) M% k% X" a, I+ E^                   ^
* B; h. L# z0 n8 s8 R) g8 R. I|                   |+ T1 q8 _7 U  Y1 h0 j2 m- s5 [' Q' |
rend() <---------- rbegin()# T/ d0 G9 F" q, e' B
   
+ i9 Z! [! U3 K- DIntVector v;$ W+ G# g& @4 |/ R2 r4 J* n: h
for( int i = 0; i < 10; ++i )
1 V/ _! g$ k0 c8 T- ?% q, ev.push_back( i );- @0 l2 Z; L: O9 t" f+ s1 d1 V8 C
   
5 J1 }- |0 Q: H; e; M# I; e& y// print 0, 1, 2, ..., 9:
3 K, N1 t$ X$ V/ q7 Wfor( IntVector::iterator i = v.begin(); i != v.end(); ++i )! L8 c( w  @; e: {
::std::cout << *i << '\n';
6 d# s7 D' u9 {2 D, Q   
% _; H2 D1 b8 J// print 9, 8, ..., 0:
4 X: u! Q  a, G1 h7 s) Rfor( IntVector::reverse_iterator i = v.rbegin(); i != v.rend(); ++i )/ @& u! U' H+ L) W
::std::cout << *i << '\n';2 K% x# @% P. d; \# c' j0 B  k  g
除了使用 begin()/end()/rbegin()/rend() 来遍历 vector 中的元素外,由于 vector 管理的空间是连续的,因此可以直接取地址进行处理:
5 ~! o+ g9 a# Y) @) S/ p
/ ?9 c* d9 U% n0 X4 ?' Q::std::vector<HANDLE> handles;
5 j0 U/ M/ O0 M* o' y/ ~handles.push_back( handle1 );
- \: t2 g. _# u0 X- U3 {6 D2 z7 o( c% hhandles.push_back( handle2 );, h2 d. F7 i9 {" O/ o1 E
) q  U) K- j$ H2 P' ~
% e) y" D$ m  x7 D" V
::WaitForMultipleObjects(handles.size(), &handles[0],TRUE, INFINITE);
- {/ |; U: d' ~& C这在与 C 库函数接口时尤其有用。
& P1 Q- u0 a, f- N8 [  N2 g$ u! H6 O* c+ o& |9 @
10. 赋值和交换
& T7 X* G; o0 b% B. d. Jvector<> 是可以赋值的,这也是一般的“值”类型必须提供的操作:& ]: }9 ^9 {, I2 V4 k: p, h
# N" V. v, Y& o+ V
IntVector v( 100, 123 );
$ P& t. E0 v' uIntVector v1;0 D( n4 a0 _- g3 c  v& W  g
v1 = v;
* z# x( D6 a8 h1 ^6 x" u4 R! C$ lvector 另外还提供了; z* ~7 q& I9 A/ V& d5 F* D/ N; `* i+ w
! ]+ P/ M7 [% {0 U% Z% C$ C
template<typename Iter>& [6 Y& l6 r) A8 S
void assign( Iter first, Iter last );( z$ m" y- C- P% g8 Q
void assign( size_t n, T const& t = T() );
9 O& Y6 s9 R* y0 m6 R' X用于赋值:# R$ C  c( W# N! a

! g+ r- @/ a! v- yint a[] = { 1, 3, 5, 7 };
4 G: h, g/ e" V* e8 b* k1 Lv.assign( a, a + 4 ); // v 将包含 1, 3, 5, 7.
  Y6 y5 m$ q* ^6 d, g3 \v.assign( 100 ); // 100 个 0。
! K2 j2 Y  X, c: d$ V2 ^) r8 E还有一个很重要的操作:
/ H9 U) Y0 a* W; j0 ^2 J; ~; D4 I1 V
void swap( vector& v ) throw();8 l0 l; F- z4 s" B
用于交换两个同类型的 vector 的值。它的特点是快速(只需要交换内部的三个指针),不产生异常。这在写一些保证异常安全的程序时非常有用。( F/ G. Y6 V" V0 y5 L! b* ^8 a+ h) Y
$ w* R% ]( D; V: R) k
事实上,swap() 基本上已经被当作类似于 operator=() 的一个“值”类型应该提供的基本操作,::std::swap() 也应该为用户定义的类型进行特例化,调用相应的类的成员 swap() 函数:! q! ]3 l9 ]+ i

7 E8 P, [$ h- n' d. W- W5 @struct MyVal
# a: H. ^/ |+ W# c{
% }0 A' y8 e7 A  // blah blah.
$ i7 s$ U( N4 F6 O: O) y$ y  void swap( MyVal& ) throw();  J  c2 n3 y' b5 Q$ y
};
- B  _- d" J* g8 a   2 L  j/ @9 P2 ^% H. ~2 t
namespace std {" }- Y/ e$ R1 [5 @8 x" r
  template<>
, z" J& u/ `/ ?) q$ @, D    void swap( MyVal& a, MyVal& b )
' ^2 H- ]8 s* d# c% }    { a.swap( b ); }+ N; r7 v1 E) t8 w" i: n/ f
}3 w1 U. A0 D' j) d
关于 swap(),值得专文讨论。这里我们只指出,vector<T>::swap() 是快速的,不抛出异常的,很有价值。
4 I2 w3 h, {' n" S& a: q* ?) Y6 q& h* S; b8 ~4 w- ]. N& L, r
11. 使用 vector 时的存储管理策略7 j4 `' J- u: N: L
从前面的介绍中可以看到,vector 的自动存储分配是指数式的增加存储空间,而且永不缩小已经分配的空间。这在大多数情况下是合适的。 如果应用程序事先知道要用到的元素个数,可以先调用 reserve() 来保留(分配)空间,这样可以避免以后增加元素时不必要的重新分配和元素拷贝:* b! w. o$ v4 P1 t  k

  ^" f0 C6 `( UIntVector v;
  E$ _. w5 Q" W6 T1 @v.reserve( 100 );
( S% F6 O; k' e( afor( int i = 0; i < 100; ++i )
$ E5 G5 g  l) q  h! V) |+ }- r    v.push_back( i );3 \1 ^1 B# g: |4 N6 S& S/ J+ H
请注意,reserve() 和 resize() 是本质上完全不同的。reserve(n) 保留的是未使用而能够使用的原始空间,而 resize(n) 是真的创建了 n 个对象:
4 W. |) I2 H3 K' a
/ `, j# i6 E, `" q- JIntVector v;- |; j/ j% l0 t0 @) w
v.resize( 100 ); // v 已经包含 100 个 0.
$ u5 ^* g& N# _1 T9 T3 r6 ?( z' cfor( int i = 0; i < 100; ++i )
1 [- W& c* [! R" ^    v[i] = i; // 可以赋值
% L+ q0 _  t3 T9 D$ j- _3 d, S有时候,一个 vector 可能增长到较多个元素,然后又减少到较少的元素个数,这时,可能希望缩小 vector 分配的空间以节约内存。CArray<> 中提供了 FreeExtra(),但 vector<> 并没有提供相应的函数。这时必须进行复制:
0 |! {0 B- l- P& T
2 Y3 m* Y' V0 F" p9 Z- {0 GIntVector(v).swap( v );
% l' ^! m. i( {9 g8 E# r有一种看法认为拷贝构造函数同时也复制了capacity(),而标准中并没有很明确地指出这一点,因此更安全的方法是. v  q& j6 m3 L
/ W: B2 u4 J2 @( k- @
IntVector(v.begin(),v.end()).swap(v);  a* I1 I* n: I, m8 Y
如果一个 vector 中可能要存储的元素个数较多(例如,超过100个),而且事先无法确定其个数(因此无法调用 reserve()),那么通常 vector 不是一个恰当的数据结构,应该考虑用 ::std::deque<>。与 vector<> 相比,deque<>不保证背后的存储空间是连续的(因此象上面的WaitForMultipleObjects()中的应用不能用 deque<HANDLE> 代替),但有较好的伸缩性,还可以在数组的前端用 push_front()/pop_front() 增减元素(hence its name, doubly endedqueue)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

Archiver|手机版|小黑屋|宁德市腾云网络科技有限公司 ( 闽ICP备2022007940号-5|闽公网安备 35092202000206号 )

GMT+8, 2025-11-14 22:07 , Processed in 0.018523 second(s), 15 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

快速回复 返回顶部 返回列表