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

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

[复制链接]
发表于 2008-2-26 13:29:18 | 显示全部楼层 |阅读模式
作者:wangtianxing; y- {0 g' n. b2 [" ]% a
3 K, v) m5 j0 Y. I7 }% {
原文出处:http://www.cpphelp.net/issue/vector.html: g. Y4 f- q9 e) T
1 V+ O0 q6 }- X7 ~8 |% n. z

; [) i6 ~" Y9 ~! L
. D% o: |  z6 Q+ B. v摘要: 本文介绍了C++标准库中的容器类vector,分析了它的优点,并且建议在应用程序中使用它作为动态数组的优先选择,而不是MFC的CArray<>等其他类模板。最后介绍了vector的接口和使用时的注意事项。
- H! G' o* L" H" \+ N) U1 @0 N9 c* e4 `
在一些使用 MFC 的程序中,经常看到许多程序使用 CArray<>,由于 CArray<>的设计问题,造成使用它的代码的复杂化,增加了维护难度。因此建议使用 ::std::vector<> 代替 CArray<>。" \8 Y& }8 m4 e

4 i8 ?6 n/ u+ P# t7 s0 L另外,也看到一些程序在用 malloc/realloc/free/new[]/delete[] 等手工管理内存。在应用程序中,手工管理内存是容易导致错误的,应该用 ::std::vector<> 之类的对象来管理动态数组。
& |$ T9 P1 f5 l% M" n/ H& m, y) _  H# Q/ D* W
由于 MSDN 中关于 ::std::vector 的内容较少,我们在这里做一些介绍,供参考。
0 Z3 ]8 ^) v" o' v6 L* ]# C& T, \% D. h4 \2 b6 Z% S
不熟悉 CArray<>/WIN32 也没关系,这里提到它们的地方并不太多。" u& {0 J" q6 y6 e0 ^
0 }$ H$ Q7 M& c' a* k
1. CArray<> VS ::std::vector<> ?
3 ?7 N1 g1 e- }1 x1 |CArray<> 和 ::std::vector<> 一样,都是模板类,用于管理任意类型的对象的动态数组。都在解构时释放所管理的动态内存。因此都可以用于代替手工动态数组管理。9 z% s: f6 j4 C0 @& j

4 n' S% e8 {* _# N8 ^: d0 g4 y+ H  R但是,CArray<> 是在 C++ 标准化之前很多年(VC++2.0时代)设计的,当时对 C++程序设计,面向对象程序设计,模板程序设计等技术认识严重不足,尤其是当时对面向对象技术的错误信仰与宣传,造成 CArray<> 的设计有重大错误。4 ]5 Q6 b) S5 P
0 A- v+ q2 A4 @) C1 \! l
在 C++ 语言标准化以后(1998),以及 VC++ 6.0 出世以后,提供了标准的::std::vector<> 模板,基本上在任何方面都要优于 CArray<>。Microsoft 由于要支持老的程序,因此一直保留了 CArray<>,但显然并没有打算按照新的思想去发展它(至少应该提供operator=(CArray const&)吧)。
6 ~5 b: w3 {- O. D% Z' d5 j. B5 g9 P! e+ E/ S; d
概括起来,CArray<> 与 ::std::vector<> 有以下不同:$ m; `( h+ y, @% w( Y
9 D) [4 u% o4 y7 V* S% Z
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<> 没有继承任何东西,只是实现了管理一个动态数组该做的事。3 N- m( x( ~" C9 D4 `

: K6 l6 R! {* q( I9 h7 Z2) CArray<> 不是一个恰当的值类型,例如下列操作都是不合法的:
: C! h: e/ }& `. c& U' x9 R$ s( U8 N6 L; ^% B( k# e  R8 I: f
CArray<int,int> a;$ N, w! r% I! j  x1 V
CArray<int,int> b(a);  // error, must use Copy().
& p% G' o3 i/ nb = a;        // error, must use Copy().: O+ l9 E8 f0 B' I! u) ^
b == a;       // error, you must write your own.: q  t( X* o* l+ z6 q
b < a;        // error, you must write your own.
5 `2 Q5 T3 z4 g0 v7 S% G* m9 y/ F+ u与 CArray<> 相反,::std::vector<> 是一个认真设计的值类型,天生是可以拷贝构造和可赋值的。如果 T 是可比较的,那么 ::std::vector<T> 将自动地是可以比较的。) e( J" f( J: [, F  W5 ]+ |* h
5 G/ S$ v5 t- D$ e, r3 ?2 |3 f
此外,由于涉及到四个特殊成员函数;+ l) J* w, ^) r9 T

7 U' g3 e9 a: x$ A( U' `T(); // 缺省构造函数(default constructor)' w2 N' `6 }8 M1 A* W  g
~T(); // 解构函数(destructor)
' V& ?9 ~' l* A: d( d7 m9 x& |; ~5 TT( T const& ); // 拷贝构造函数
& m% t; H! s- }% vT& operator=( T const& ); // 拷贝赋值函数
) D6 E0 B6 E$ Z的自动生成,如果使用 CArray() 作为 T 的成员变量,那么上述的四个特殊函数中的后两个将无法自动生成,需要手工写:
% k7 u! k% m! [" c
9 b: y" S3 N; C1 z* E- } struct T
6 u  D: f# A  R9 N3 u0 `{5 q8 _' n! c% h
   T() {}! U/ M2 l  c" i) Q* t$ A; D
   T( T const& t )
( o* o; |+ Q% A( B. y9 u8 D! N8 ]" H& U   {
! o$ _) p" q6 Q6 k7 ]0 C       a_.Copy( t.a_ );7 c6 |; s' J) }1 o- r  Q6 n9 C
       i_ = t.i_;( T% J3 S) k# l
       d_ = t.d_;
; w1 s; e+ e) u0 g& e+ L- U       s_ = t.s_;; J5 V8 i; C3 n- w4 T( G
   }
; I% n! x" g9 C7 v7 e7 \0 u   T& operator = ( T const& t )7 a2 {# _3 s8 r3 p2 K
   {
! K0 h; Y( O7 H' L       if( this != &t )
' B& D' o8 J( J+ ]; d$ I  @       {
) H8 K  R+ f9 r0 G) S$ e, B2 @           a_.Copy( t.a_ );
) G9 X/ P" [/ P$ }% ]$ o           i_ = t.i_;- e2 y) S* k' R
           d_ = t.d_;
: Z# i+ T' k+ T4 w           s_ = t.s_;
: L  E  P. `( d) E$ h3 e% F       }0 q( `8 ]6 y  ^" v# M; Z
       return *this;
5 O3 L6 K9 r6 A) t   }8 v& A. a% j  ^: y6 {6 m2 o
private:
, w6 w2 _( C" s0 x- v( P   CArray<int,int> a_;
8 x9 t7 S9 K$ y/ Z- ^  p   int i_;$ y) L) Z9 M- X/ y, ^
   double d_;" O9 `, A" I  Q
   ::std::string s_;
! E" X9 u& z/ x3 I( `};
: K( d( Z( L! e1 n" h* C, _, I2 R如果使用 ::std::vector<>:  B3 S1 U* a$ P. e$ c
& u. o$ G4 |. ~4 ^2 ~1 K
struct T
1 }  K/ `6 Q; s/ U& U5 X5 h9 s{
& q( G/ o3 a9 Uprivate:
; k7 Z- u- A( o: x! a' F$ i' A   ::std::vector<int> a_;
4 x. a4 G+ K; G, m. z   int i_;& Y0 X4 |9 M, F3 @- Y* M# F
   double d_;* R1 P( ]/ `/ ~) ]# \
   ::std::string s_;2 k5 z  z# Z+ X5 V
};4 u+ |/ S" H+ e( a$ f, T% c6 Z
上面列出的三个特殊成员函数都不需要写。好处是明显的:当你增减 T 的成员变量时,你不必到; t5 `& \0 M$ y6 i! k$ j& p: P+ H
T(T const&) 和 operator=() 中去相应地增减。
+ L+ z6 K5 R2 l% e3 W2 Q9 J  }" X; K; p* H! j4 J# F
3) 没有现成的算法可以对 CArray<> 进行操作,而标准 C++ 里的标准算法大多都可以直接在
& m4 d7 J3 v( H- j::std::vector<> 上运行。例如:# N  ^7 K3 r/ b- ?3 ~, Z9 q1 ~
1 F5 h5 w. E& d& t7 j* c0 Z
static int const init_vals[] = { 3, 1, 4, 1, 6, 9 };2 o! J& R! ~  F1 N7 b0 S9 |1 Y
vector<int> a( init_vals, init_vals + 6 );
; d/ P) a) y7 n. b' m*find( a.begin(), a.end(), 6 ) = 5;    // 把6改成5+ z  Y6 y/ Y- k5 P
sort( a.begin(), a.end() );    // 排序。. ~( l. s! V* [# ?. e" A
可以说,CArray<> 的主要设计错误是把一个本来应该是一个简单的“值”类型的东西设计成一个难用的“对象”类型了。所有的“值”的好特性都丧失了,但那些从CArray<>继承的派生类呢?- Z7 R+ |6 ?  F5 B
8 g2 a9 S; C7 M4 W6 M- ~9 q! O
CByteArray等的问题与 CArray<> 的问题一样,甚至更多(例如,CPtrArray,永远不要用)。
% Z( f9 _" l) j5 l/ _4 e* h( C2 b5 l
同样,其他的 MFC container 模板,象 CMap<>, CList<> 等,都有类似问题,都应该用2 K- y$ }7 t% @* ~3 w
::std::map<>,::std::list<> 等设计更好的东西代替。* h; Y. C+ ^- A% a! v

; v/ o% l7 u: A7 U0 X7 K1 v! ]3 ?' w2. ::std::vector<> 在哪里?
: r/ W( v1 c) x1 ^/ J/ {2 z, s::std::vector<> 在头文件 <vector> 中定义:  s( G6 J. o% k. f! I0 e
5 }' r! a7 _( G2 \3 k1 v
(注意,标准的 C++ 头文件都没有 .h 后缀,有 .h 的文件是与 C 兼容的,或支持老的不标准的东西,象 <iostream.h>。)
6 O6 d3 h# B- q0 i2 E% i& j
# d) R2 t1 t! }namespace std * d* g6 p, b0 v) }2 d2 Z
{6 B/ N+ n( n- j
    template<typename T, typename A = allocator<T> >
! L" f: Q" o5 F, L7 W  A    struct vector% Q* C# X2 r; r+ y4 r0 X: M+ p  t) G
    {8 u4 S3 m3 H; C3 L/ E
        // 具体内容稍后讨论: J2 [; X" O2 D9 o& F' N( M
    };  C$ q9 W2 M; j5 V& \6 L2 h+ j
% x. M+ K; \0 Y, {
/ Y  \1 j# @$ w! [5 O# L
    template<typename T, typename A>
( P5 B- G" ^7 z        bool operator == ( vector<T,A> const& a, vector<T,A> const&    b );5 r# ~, f  z  W# [* |; c% Q
    template<typename T, typename A>
! E5 Z/ h  \; b) x3 x4 [% I1 K        bool operator != ( vector<T,A> const& a, vector<T,A> const&    b );
) c% G$ ^" `, @/ A0 D2 q4 u' u    template<typename T, typename A>, d+ p3 ]" b) |
        bool operator < ( vector<T,A> const& a, vector<T,A> const&    b );
* {$ k' w9 J/ n' g3 v8 X+ t5 M9 j    template<typename T, typename A>
/ f9 X. d1 ~8 J        bool operator >= ( vector<T,A> const& a, vector<T,A> const&    b );7 M- A# r9 ^. c3 v" U4 m
    template<typename T, typename A>8 S3 V7 f4 b7 f4 P0 H2 j; ]
        bool operator > ( vector<T,A> const& a, vector<T,A> const&    b );1 H* i6 R  E. |8 B1 {
    template<typename T, typename A>0 E; ^: Z+ T/ e) Y0 K
        bool operator >= ( vector<T,A> const& a, vector<T,A> const&    b );
1 y) C# A- E% u" ~$ w# |+ c}
' b" r# S* C/ w6 p' Y, Tvector<> 定义在 namespace std 中,使用时为了减少击键次数,通常使用一个类型定义缩短类型名称:# j0 B. y& c2 j: u: j  i
* \% J/ p9 d! Q) q% c. a3 R6 [; N
#include <vector>$ Q# j+ {$ o) l
typedef ::std::vector<int> IntVector;0 {8 j6 `5 i. ], M, [
IntVector a;
) x2 b) s' A0 J# S! L/ U3 b7 S* aIntVector b( a );3 P. Y8 l8 z. k' y4 D+ J  J
IntVector c;0 w, }4 K! W: w5 a/ S8 ?0 P
c = b;* s, J: S) p$ O- q) j) a8 f& z
assert( a == c );8 t9 v2 n2 m! L0 _" N. X
请注意 <vector> 中定义了六个 vector<T,A> 的比较函数。这些函数只在真的用到时才会被实例化,才会要求 T 也提供 operator==() 和 operator<()。
/ S6 h. W$ B3 a. o4 D6 V
* y" f- x, [% M% O另外,A = alloctor<T>:用于提供一个用户定义的存储管理类。由于这个参数很少用到,而且在 VC++6 的实现中有问题,不能用,因此以下的讨论忽略这一部分的内容。
9 C+ H. x* _0 J2 E. s* M; n. W  e- u9 }6 Y. }1 O  @4 `: \, n) O
3. ::std::vector<> 中的类型定义
- k9 y: S% \$ J( b+ l% t8 {  svector<> 中定义了一些类型,下面只列出常用的:" {* t6 @- g' j+ X3 w
# [/ c0 a4 E8 @- m- b/ n: Q6 x8 s
typedef T value_type;3 {  G. H) U" P5 y" S
typedef T0 iterator;4 w& b. f- n# \
typedef T1 const_iterator;( D6 m  C6 l; o8 f% T0 Y
typedef T2 reverse_iterator;- s" h. p, R$ X$ B; H, |
typedef T3 const_reverse_iterator;
8 c9 A; n  d9 D) P* t; {% Y  Q7 C- K0 n9 @- s# O
value_type 就是 vector<T> 的元素类型,也就是 T。当写通用的算法处理任意类型的 vector<> 或其他容器类型时是很有用的。
% t& a, o2 L; {& F; ?9 K, P3 A
' a! a" F) c6 r) Citerator/const_iterator 是两个 vector<> 的实现定义的未知类型,用于访问vector<> 中的元素,类似于 T*/T const* 指针,他们的区别是一个指向的元素可被修改,另一个只可以读:3 O% A& c" O/ D, ^
* a$ q! ]" m7 j# C" L
typedef ::std::vector<int> IntVector;
1 ^- ~4 E; W. S* c2 hIntVector::iterator iter;
9 g8 c! a; m# F' YIntVector::const_iterator c_iter;4 n3 _& q$ c1 p
// ...
4 d2 b& @2 m# {6 f. X3 [+ ^: h++iter; iter++; // ok: increment, post-increment.
$ X' u- w7 M! G! L7 @5 A4 d3 c* u--iter; iter--; // ok: decrement, post-decrement.( F" b" _: ^8 x' _, a0 i
++c_iter; c_iter++; // ok: increment, post-increment.1 f7 S- H8 e* U# a
--c_iter; c_iter--; // ok: decrement, post-decrement.
: [1 k4 P# A+ R! _5 n*iter = 123; // ok.
/ G/ i' v' s3 ~int k = *iter; // ok.& _4 H) s5 `% A
k = *--c_iter; // ok.) ~2 N+ x2 P, G, a. ]) _# a9 {
*c_iter = k; // error.5 Y/ N8 M4 h" d$ D* a; q
c_iter = iter; // ok: iterator is convertible to const_iterator.
2 V3 E( D5 p: _# e8 }5 I4 C$ eiter = c_iter; // error: can't convert const_iterator to iterator.# B9 X9 u3 @* l
在使用上 iterator/const_iterator 和 T*/T const* 基本相同,事实上有些vector<> 的实现里就是用 T*/T const* 实现 iterator/const_iterator 的,但又不可以把 iterator/const_iterator 当作真正的 T*/T const*:$ @4 F( Y# A  K  E

; D+ H6 [/ w4 H3 Z, c  MT* p = iter; // may fail to compile.
" d, b8 S2 F! _  Z4 ]9 n, iT const* q = c_iter; // may fail to compile., p* R' J7 r) x# J3 L$ l$ Z
reverse_iterator/const_reverse_iterator 与 iterator/const_iterator 类似,但以相反的次序(从尾至头)访问 vector 中的元素。1 \! X. \6 x4 y! A) V5 ?9 Z1 e
* v* p( m' l9 V$ D( a+ g
各种各样的 iterator 在 STL 中有特别重要的意义,但这里我们不做具体介绍。只要理解通过 iterator 可以访问 vector 中的元素,大概相当于一个指示位置的指针就行了。
; }1 V8 T% N. x! D  D& [6 i6 W- Y4 }+ M: A
4. ::std::vector<> 的构造  Q0 @$ W8 z: e, l- g- w
vector<> 提供了以下构造函数:(忽略 allocator 参数)
( Q: p5 e0 B) q5 Y5 f! K& o/ h0 M, r3 q/ x. z" q; ^1 \
vector();% B& h* K4 u8 P/ `* Z% G
vector( size_t n, T const t=T() );
# y" O, G4 j1 D1 J+ H; ?vector( vector const & );
; ?% a: G- [$ a8 u( s2 kvector( const_iterator first, const_iterator last );
& K- K, f: e$ x3 q# [, D9 y1) vector();, l- d- I5 h( K1 Z9 \8 }) b* G1 r
6 n2 Y! N" v3 @) \
构造一个空的 vector,不包含任何元素。* \* q0 B1 N, }; l+ O  s6 i
" w( h' w; p1 F
IntVector v1; // 空的整数向量。
4 Q  h5 }" `1 W5 s6 E2) vector( size_t n, T const t=T() );
1 |( E) `& D+ _1 ~5 |4 Z
5 o- W/ d4 U& g0 s构造一个 n 个相同元素 t 组成的 vector。如果不给出 t,那么将用 T() 做缺省值:* b8 c* H- j' A6 ^

  i: _' \. T7 I% Y4 ~IntVector v2( 100, 1234 ); // 100 个 1234.
- A# o6 h' b% ^7 GIntVector v3( 100 ); // 100 个 0。. Z6 `2 @& u! T+ a
3) vector( vector const& other );
& k8 i/ }% S* K2 g6 e; E
1 ^5 _. N% L& m复制构造函数,复制 other 中的内容:( `! `: {5 r- ^( D
% C" Q& I; o2 {
IntVector v4( v2 ); // 100 个 1234。' ^' |. ?2 T! h9 ?* ]) }( n
4) vector( const_iterator first, const_iterator last );3 j7 w# R2 j% o/ w
  V: T6 }- `# q; N9 S. `
事实上,这个构造函数应该为8 h% a' m- e  B% {6 Z
8 W1 {# g. n& w3 r! ?7 u" S7 h! Y4 W
template<typename Iter>+ s$ y* }0 V* S) [6 n# [/ j/ |
    vector( Iter first, Iter last );
; f$ \9 B, U( d- F+ p即拷贝任意的序列 [first,last) 到 vector 中。由于 VC++6sp0 编译程序的限制, Iter 被换为 const_iterator 了。不过,碰巧 const_iterator就是 T const*,所以可以如下使用:
7 b' l/ l5 v' U5 }1 ^6 k; L6 [4 m
int a[] = { 1, 2, 3, 4, 5 };- I: _& H$ Y$ F5 u, q/ I- U
IntVector v5( a, a + 5 ); // {1,2,3,4,5}
) T5 ^6 K7 A+ {' l+ rIntVector v6( v5.begin() + 2, v5.end() ); // {3,4,5}
* k* S5 h& S9 ]' _4 O5. 访问 vector<> 中的元素+ X. T, E( S. i
以下成员函数/运算符用于访问 vector 中的一个元素:
- A1 G. z4 \5 ]
% O3 O  A' j( E$ ]+ j' fT& at( size_t n );1 d# ~* N, e7 R  [- Z! `
T const& at( size_t n ) const;: o6 z/ }* ]1 s1 O) n" |* r1 U: T! [
T& operator [] ( size_t n );
" \' T( S0 B) P; _4 p2 bT const& operator [] ( size_t n ) const;
; d( V0 P* g' W3 ?' i* g8 C+ LT& front();: h) h- g- n$ N/ U/ z& P
T const& front() const;/ ?! L: b$ R+ u! W3 C- N9 u8 w+ J
T& back();
) E2 ~; W) r9 m) |7 ]! T" F% `' ]T const& back() const;
5 R. y0 D9 t9 G+ J& V1 E请注意,由于 vector 是一个“值”语义的对象,所有的操作函数都必须严格保证 const 的正确性。所以,所有的元素访问方法都有 const 和非 const两个版本。
6 E. C: D$ m6 E4 n+ G, @6 Z+ N
- w: g( c& H+ Sat(n) 和 operator [] (n) 都返回下标为 n 的元素的引用,他们的区别是,at() 进行下标越界检查,若发现越界,抛出 range_error 异常,operator[]不进行下标检查。
. l  G; d8 l7 [% U5 B9 ~7 D7 f4 ]3 B& Y6 Q1 |, {% \% F5 T
front() 返回下标为 0 的元素的引用,back() 返回最后一个元素的引用。
3 @9 Z* [) i0 g) c3 M) p) Z5 @! q/ r' Q% B2 F! F2 C4 P
int a[] = { 4, 1, 4, 1, 5, 8 };& S) X8 o, }+ i
IntVector v( a, a + 6 );
$ I+ I: I# d, E. v// 使用 front(), back():
9 W( n) m! |  g6 t2 W6 J- nv.front() = 3;
6 o  m. g% T* @& S- J2 R, [+ lv.back() = 9;
, V' h: }  n; z# r// 使用 operator [] ():# `0 y- }* U0 v% _: w' `
for( size_t i = 0; i < v.size(); ++i )
+ `4 j1 x) E1 R- |::std::cout << v[i] << '\n';
& q/ d8 |6 I% r0 ?, R$ {( x6. ::std::vector<> 的存储管理
/ Y9 l0 B# m7 v. P& d" C以下成员函数用于存储管理:
+ l" q, a% A" H( o2 ?) E
8 Y" f4 A! N8 h( S4 a8 ^void reserve( size_t n );
, W) I: B3 G2 Csize_t capacity() const;
) I+ `1 |8 [, d3 pvoid resize( size_t n, T t=T() );
& c& X/ N1 ^) {" p3 _7 u0 @4 `void clear();
, \/ d5 ~- I( j( jsize_t size() const;, e. B0 q' [+ K+ I6 z0 d
bool empty() const { return size() == 0; }
3 }( X5 n& a2 H1 h) E* W1 ~) Ssize_t max_size() const;, H% J1 f! M7 ^( {

5 z, L: h7 h; _# {" d* g% j另外,push_back(), insert() 等也涉及到存储管理,后面另行介绍。  v! z: E0 `( q4 |

5 ~: ?$ ]( V) r8 M) w1) max_size()
/ l: t) J% t3 T3 m" {
$ S) R" V, t5 |返回 vector<T> 理论上可以装的最多 T 的个数。这只是一个理论上的数字, 大概是 4GB/sizeof(T),没有多大实用价值。在程序中不要用。
- _4 a1 Q( d8 \
3 b! s- o$ E6 W, A% v2) size()
0 y' O$ ~$ d2 ^3 ~. |! }; A$ A$ I: U. N  K9 e8 v/ I
返回 vector<T> 中实际装的 T 的个数。相当于 CArray<>::GetSize()。
% ]& _8 w& ?# K7 o5 a8 O% v+ E$ {$ S& C, l
3) empty()6 b5 P( a' o5 g) @$ A8 A

5 M4 }. m/ b6 o" V% o如果 vector<T> 中没有任何 T 对象,返回 true。也就是返回 size() == 0。% g) U8 F( \6 a$ i  L
) ^% z* l1 t. g8 k6 b, ~4 u
4) clear();
8 Q# ?; }0 m7 u
" O7 g& P% m  s2 @5 N6 l+ V) x清除 vector<T> 中的所有 T 对象。执行后 empty() 返回 true。大致相当于 resize(0),但不要求 T 可被缺省构造。相当于 CArray<>::RemoveAll()。
% `) b' j- B/ Y& I) b2 k& b: M. R. m; A1 v& V
5) resize( size_t n, T t = T() );  J1 p7 I3 ~+ w* x3 {  {

& T, }5 W  N5 y3 [6 N5 u将 vector 中的元素个数设置为 n,n 可以大于 size() 也可以小于 size。如果 n 小于 size(),那么 vector 中下标为 n..size()-1 的元素都将被解构。如果 n > size(),那么将在 vector 的后面新增加
( h3 Q$ ?2 J% U3 ?- X' Sn - size() 个相同的元素 t。在增大 vector 时,可能发生存储再次分配。总之,调用resize( n, t ) 后,(size() == n) 成立。  X- v( R4 C7 F

. e+ H. J2 L/ @: w: Q* j请注意,如果调用 resize( n ) 不带参数 t ,那么 T 必须可以缺省构造。
8 D7 d: v/ U) s- u9 D8 N7 `5 Z( v% H1 ?! k* k; p# Q+ ?  K) R
6) reserve( size_t n );( r; O; {6 i" Y* A; P
+ d6 j  ^( w" h2 ?
事先分配至少可以保存 n 个 T 对象的空间。调用后 (capacity() >= n)成立。0 I* ?5 h: p, N) p' j3 ^
4 U: D- q$ w& ?+ B- N  ]; C! i
7) capacity();: {$ r  p/ o; G6 B. d, [4 I2 K7 Z
; b3 c3 y, R! n$ ]( O( f  I
返回已经分配的存储空间够容纳的 T 类型对象的个数。后续的增加元素操作(如 push_back(), insert())如果增加元素后 vector 中的总元素个数不超过 capacity(),那么 vector 的实现保证不重新分配存储空间。2 c* F/ K; ?3 q  Q) {3 C: Y

5 E3 R3 L' D. k/ wvector 管理的动态存储空间是连续的。执行操作
) N$ F2 I- |% n
6 m7 R2 s3 |3 P- T3 q1 H2 CIntVector v(7, 1); // seven ones.
/ [% E1 A1 m4 Z- Y# rv.reserve( 12 );! K. ]% f. I1 }& A
后,v 的状态可以用下图表示:
5 k: `* {( _' H5 f# |6 @, Y. u& O) S" Q/ F
/--size()---\& U3 N) s' A" ]6 u
|1|1|1|1|1|1|1|-|-|-|-|-|5 X# j% }% E# v/ M0 p! _
\--capacity()---------/
; C. a/ v. d6 t' k) O8 W, U其中,1 是已经构造的 int 类型的对象,- 是可以构造一个 int 类型的对象,但还没有构造的原始空间。再执行$ d6 A: M3 p7 e0 p7 Z# j/ Q& e
7 D6 n4 e- ?" r' y  D  _/ R
v.push_back( 2 );* j& x* b# w8 s3 g& W2 d9 s
v.push_back( 3 );; t+ x1 T! M4 r
后,v 的状态可用下图表示:1 i0 k! t7 _0 X% y2 }5 N1 ^+ \/ G$ @) k
1 {1 k& m& z, U' \: q' N3 C
/----size()-----\# K& l: T0 r  q4 w5 \- A
|1|1|1|1|1|1|1|2|3|-|-|-|
1 v) M, J1 ]5 {5 g' @/ J9 { \----capacity()-------/
, V  K( V& K8 y7 c- R( G执行 resize( 11, 4 ); 后:
% s# t$ a4 L6 x8 v, j
- m) o5 K! p" o& ], V1 S3 e3 F( z4 |- L /----size()---------\0 e0 n% v' p) z7 V& M5 u9 g
|1|1|1|1|1|1|1|2|3|4|4|-|
; {$ `% H& r- {, `9 l \----capacity()-------/7 H; C; |5 H$ H' @
capacity() >= size() 总是成立的。对于下标为 [size()..capacity()-1]的未构造对象的存储空间,是不可以访问的:
  w- Q: i4 Y/ `( N1 @& f# k9 p
& j/ N6 L+ O2 B1 ]! u5 Bv[11] = 5; // undefined behavior - anything can happen.8 n! S! b2 b8 o3 S) {
7. 添加元素到 vector 中; I8 }2 A7 ?: T+ o" P7 W
下列操作添加元素到 vector 中,并可能引起存储分配:) l7 v4 q8 w) j7 f5 \+ |$ G
0 V$ o6 ?2 u, m4 v7 c$ z8 M
void push_back( T const& t );
& i% I0 U/ ^4 ]) A" K6 Vvoid insert( iterator pos, T const& t=T() );" o: a. @, {! b- w8 F- I8 ?8 Q
void insert( iterator pos, size_t n, T const& t );, ~1 t5 x6 f0 o( y( f
template<typename Iter>5 P8 r$ F) y$ ^5 u( I# @! Y2 C
    void insert( iterator pos, Iter first, Iter last );
5 k+ F2 n2 C  g2 Vpush_back() 是把一个元素添加到 vector 的末尾。insert() 是把一个 t,或 n 个 t,或从 first 开始到 last 结束的一个序列插入到 pos 指示的位置之前。( i* Q# \3 ^4 f! X: m
' {& \% M$ Z# l8 S
当插入元素后 size() 将会大于 capacity() 时,将引起自动存储分配。vector 将会分配一个比需要的存储区大若干倍(通常是1.5到2)的新的存储区,把老的元素拷贝过去,同时完成添加或插入,然后释放老的存储区。
% t/ j1 a- Z* R/ @1 T
; e" Y9 ^* Z' C, \7 \7 R# f这就是说,vector 自动存储分配的空间大小是指数式增长的,这可以保证多次添加元素到 vector 中时,平均用时是接近于常数的。
' |- B6 y) X5 z" H) e3 o5 b( x* Q! {
IntVector v;5 T8 m6 }: ~. a% k# M" M; D( j
   & _9 n) c) @& a9 R
// add 0, 1, ..., 99 to v:% U( o; K5 z/ r. j2 ?7 O
for( int i = 0; i < 100; ++i )
' E& l  V- ?' ^9 X  `2 t7 j, C6 }v.push_back( i );
: }. C9 Z; H) I9 w   
9 p  D# F/ N/ ]# w9 L// append 9, 8, 7,..., 0 to the end:$ x; h$ B6 k  z6 |1 {+ V; N
int a[] = { 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 };) M1 G8 g+ t9 @# W8 M; t# H
v.insert( v.end(), a, a + 10 );% w5 y# N9 x! Y: C6 g; U0 e( B
8. 删除元素" H8 N( u) Y6 t+ _$ G2 `
下列成员函数完成元素删除:6 s" u) Y' X; s1 j3 N
7 A- t& M2 a* Z6 X( r6 z2 y
void erase( iterator );
) I9 L* G4 C6 I1 P6 w1 K! N& u9 ]void erase( iterator first, iterator last );/ ^6 }( Y) v# Z+ P/ A$ j" @1 `) b
void pop_back();# ~7 L% f$ H, P4 o* O
void clear();
% a$ R! |) K# V5 Q; j7 _: z7 w这些函数分别删除一个,一串,最后一个,或全部元素。
7 X' e( [. T& M: z8 {  v; E' q3 ]8 H+ Z5 S
IntVector v;
% t! ^9 V' d5 ^3 c- w/ [for( int i = 0; i < 100; ++i )" ~5 t1 n/ Y4 A7 S8 j
    v.push_back( i );
! J* j" n6 x4 \' w3 V4 I: ^   2 v6 |* C: k" Q* p& p1 v. h
// 删除 50, 51, ..., 89:5 L' g4 z$ i! C5 h9 r/ _
v.erase( v.begin() + 50, v.end() - 10 );& {5 q4 U  ~' {5 V6 m
   
4 Q/ z& ]1 |6 u8 M5 }// 删除 49, 48:( J. k# t& Y' s3 T
v.pop_back();
, l  \6 R" ]; C' L2 D9 @& kv.pop_back();
6 ]" T+ i4 ^+ L   + E6 x/ Q# N- V- H- I! ]1 C  Q
// 全部删除:
, D" l! ]9 I# T6 R7 Fv.clear();' e" f/ Z3 g. W: S8 X
注意,删除操作不会引起存储分配,因此 capacity() 不变。
6 t8 p5 e! u4 g" B0 g# \  z2 b# t4 y! m+ n8 L
9. 作为序列访问 vector 中的元素
9 n8 D. ]! _7 P. t序列(sequence)在 STL 中是一个非常重要的概念,所有的容器类型和算法都涉及到,而且所有的算法都是建立在“序列”这个概念之上的。
: q- M  A" g6 ?
* x0 g" m( d# A“序列”是一个线性结构,由一个指示其起始和一个指示结束的叠代子(iterator)来决定。如果 first 和 last 是某种类型的叠代子,那么经常用[first, last) 来表示一个序列。注意,first 指向的元素是这个序列的一个元素,而 last 指示的是这个序列最后一个元素之后的位置,可能根本没有元素可以访问。这种半闭半开的区间表示是整个 C++ 标准中的约定,而且确实可以简化程序。
( \8 H0 z5 q, X. v  ~, O; c, L  k2 X
* P! t& i% l" z" |$ F1 |# l9 X叠代子是传统的 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 的要求。2 Q0 b) u  D/ n1 J/ e
% B3 |  I8 T$ L4 k) ]+ C1 O1 H; \
vector<> 中定义了以下函数用于获取被控制(管理的)序列(动态数组)的各种叠代子:9 u. Z; Z/ o( N  ^/ O7 j
) e2 X  L' S2 g, C. z* x
iterator begin();+ z' x2 T" n. s" K. t7 u
iterator end();
2 K) u0 @, k5 g5 D3 uconst_iterator begin() const;# \5 @, k8 z, c% V& r- W
const_iterator end() const;9 P* q: r) @6 n4 O
reverse_iterator rbegin();
- P/ J) O0 E+ J' L: L/ Jreverse_iterator rend();- W4 B  Z* d$ I2 x% F- ?  h7 @
const_reverse_iterator rbegin() const;
) z9 H% q' z' m( Kconst_reverse_iterator rend() const;
3 Y( M- X3 B, g. O这里我们不讨论叠代子的一般概念,只举几个 random access iterator 的例子:
- v" |% a* [& h5 h3 B, X4 |+ p8 R3 p' B! j8 H# ?/ f& d& o
int a[] = { 1, 2, 3, 4, 5, 6 };
& q1 J4 d" C  c& V3 D( v[a, a + 6) 是一个随机访问序列,指示了 a[] 中的所有元素。这里叠代子的类型为 int*。
5 P3 V; [! _1 }. {/ ?: Z' W; H( m8 U2 {5 X) N5 m' ?7 N4 f4 t
[a + 2, a + 4) 也是一个序列,指示了 a[] 中的 3, 4 两个元素。叠代子的类型仍然是 int*。
8 E: O5 B+ J" ]9 P  B4 v3 G2 Q
- x8 X; U, d0 WIntVector v( 100, 1 ); // 100 个 1。7 N) |6 N4 d+ ^1 [9 }/ Y: ]: ~% V
[v.begin(), v.end()) 是一个随机访问序列,指示了 v 中的所有元素,叠代子的类型是 IntVector::iterator。; U; }  B' {) o$ g, g+ o1 J
7 ~5 S; N, l& C) k+ r; l
[v.begin() + 10, v.end() - 20 ) 也是一个随机访问序列,指的是 v 中除了头 10 个和尾 20 个元素外的其它元素。# A+ D6 m' v6 |( f2 f6 x) l
/ w+ h# ?' z% b3 K7 S
[v.rbegin(), v.rend() ) 是一个随机访问序列,指的是 v 中的所有元素,但与 [v.begin(), v.end() ) 不同,这个序列是从尾到头遍历所有元素。
9 c. j9 L0 w' N3 g& q; V7 q6 O2 X2 |( X5 u! N7 T
[v.rbegin() + 20, v.rend() - 10) 与 [v.begin() + 10, v.end() - 20 )指示的元素相同,但遍历顺序相反。1 I$ }+ X: z& r) q. r5 q2 A

3 L4 P% T6 R  R下图是有十个元素的 vector 的 begin()/end()/rbegin()/end() 的示意:  P) X& E* O, n9 x) n6 b
" q, e6 s  h% c' J1 S
begin() ----------> end()
0 s- x% Z% B$ K  |                   |
' ^( P; |* x- R/ b& }  v                   v
4 |5 U4 y6 {  ^# L6 X9 T: K  e  ] |0|1|2|3|4|5|6|7|8|9|6 d- q, P) E! u- v/ {/ y
^                   ^8 t3 |. H$ A: \9 ^# n% R
|                   |
  r" h' e0 z# V; M  e+ Xrend() <---------- rbegin()
' c4 ?6 b; m. E6 L3 v! w1 ^- ]: j1 T   
7 |( h- C8 X. l  x0 WIntVector v;
) w7 z6 U( j, ?  o) kfor( int i = 0; i < 10; ++i )
( Y# m. v  |+ k- @4 u7 Ov.push_back( i );
) w/ N3 M6 I6 `3 D* B5 R   # }7 m/ R; |+ L. {6 R& E
// print 0, 1, 2, ..., 9:
1 g; ~3 p0 Z7 |- l+ ofor( IntVector::iterator i = v.begin(); i != v.end(); ++i )5 z3 ^" w: c$ z. y9 D$ ^
::std::cout << *i << '\n';4 R  C/ t. m. M' U/ H
   ' W8 R) T; [) ^% T7 N; n6 ^
// print 9, 8, ..., 0:
" G. V. I. W7 r" Bfor( IntVector::reverse_iterator i = v.rbegin(); i != v.rend(); ++i )+ n* |+ S! _( {# n$ D3 E4 C8 ?
::std::cout << *i << '\n';
3 K# K; ~+ I& |* h除了使用 begin()/end()/rbegin()/rend() 来遍历 vector 中的元素外,由于 vector 管理的空间是连续的,因此可以直接取地址进行处理:
" Q  E* w# l- x
. }6 i1 w, r2 K::std::vector<HANDLE> handles;
/ m0 R5 ^3 r# ]handles.push_back( handle1 );
) c( J' r4 _$ Yhandles.push_back( handle2 );8 C: d  r" L( o+ P, `( j) K
- |; Z* P& D/ x' E& q, T7 z
6 p! j( ^: m! o  t4 [/ s* @
::WaitForMultipleObjects(handles.size(), &handles[0],TRUE, INFINITE);
- S3 @1 [. {( F- v* O, @3 W9 _& b0 G这在与 C 库函数接口时尤其有用。
9 N" }% c# @1 n% ^
) L9 j9 F7 D) @, ^1 v* q10. 赋值和交换' j6 k( q3 o9 K
vector<> 是可以赋值的,这也是一般的“值”类型必须提供的操作:+ f( w% Y$ @, i& H; K

9 b! r! L- P( zIntVector v( 100, 123 );
# H7 j7 ^. V* A8 Y' bIntVector v1;
5 H& |/ l: B0 \  {v1 = v;
; u5 O4 O5 Z: o: t8 _vector 另外还提供了) e& b) ~0 U: C& D" ~, t1 N. t6 s

* E3 r* |7 g$ [7 Dtemplate<typename Iter>/ R& s+ E4 H; E# V# x0 e
void assign( Iter first, Iter last );
. u2 j4 m7 H) @' n) Y1 b/ j" Q0 Ovoid assign( size_t n, T const& t = T() );
& h% P0 B9 f2 e8 }4 _用于赋值:
1 ?' Q  o6 B) B: g( o
9 T4 v7 v+ [4 _int a[] = { 1, 3, 5, 7 };
4 Y4 X' q0 X: q0 {* n: q8 c1 e- `" Uv.assign( a, a + 4 ); // v 将包含 1, 3, 5, 7.
! v9 _" h* j& ~: X5 e- O# Iv.assign( 100 ); // 100 个 0。
$ @) A- e+ U) h0 F5 Y; p! B3 H6 T4 o# I还有一个很重要的操作:4 ]/ q- n) q0 C3 `4 |  x
4 P1 X, u- |& b0 S2 Q: O& S2 n) b( N
void swap( vector& v ) throw();
0 s* j; n7 \) ]9 ^用于交换两个同类型的 vector 的值。它的特点是快速(只需要交换内部的三个指针),不产生异常。这在写一些保证异常安全的程序时非常有用。
1 X+ K* U! @, @5 h/ J3 k7 p1 j  W) R+ B& T+ b
事实上,swap() 基本上已经被当作类似于 operator=() 的一个“值”类型应该提供的基本操作,::std::swap() 也应该为用户定义的类型进行特例化,调用相应的类的成员 swap() 函数:4 {( g- i3 u' U2 |" d% O2 k4 H+ z

' j7 B4 ]8 C5 m1 W$ qstruct MyVal% y& n6 u  O) f4 U
{' F% o% o: i& \- b- g4 y3 {$ O0 H: T
  // blah blah.
  f- D2 j: ^: b' a  void swap( MyVal& ) throw();9 p7 Q* e$ O% ]# j& i' j0 e
};  v9 U# e: I; m9 d
   9 g% d, V! F0 a* L& {( W  s
namespace std {3 C4 J: |& ]) q
  template<># F( L& [4 G( c, }( l$ D
    void swap( MyVal& a, MyVal& b )% K/ i& u6 u# ]8 ^$ w
    { a.swap( b ); }0 X2 Y6 I0 |( A% ?
}
. P! ~& l  J% R+ d0 G' }: O2 l关于 swap(),值得专文讨论。这里我们只指出,vector<T>::swap() 是快速的,不抛出异常的,很有价值。9 K+ v9 m- U) e
. D& W* d; p# C, u4 S' P/ j
11. 使用 vector 时的存储管理策略
: Z! I* n& t1 d从前面的介绍中可以看到,vector 的自动存储分配是指数式的增加存储空间,而且永不缩小已经分配的空间。这在大多数情况下是合适的。 如果应用程序事先知道要用到的元素个数,可以先调用 reserve() 来保留(分配)空间,这样可以避免以后增加元素时不必要的重新分配和元素拷贝:6 u0 F; A# p" Q$ N9 }; F0 Q. p

3 U. o( H/ b9 u  s: m! LIntVector v;' T6 m1 M. J# S+ R; v6 p. n* E
v.reserve( 100 );
9 [& J& v: q+ o; P, \+ Rfor( int i = 0; i < 100; ++i )' C2 g3 Y# Z7 l* L5 |5 [
    v.push_back( i );
- i0 g9 R+ ^9 T) E请注意,reserve() 和 resize() 是本质上完全不同的。reserve(n) 保留的是未使用而能够使用的原始空间,而 resize(n) 是真的创建了 n 个对象:
$ L, ~: c: P9 \9 U& X# _2 z
# ^7 L0 X8 q$ ]% c7 c4 hIntVector v;
/ v. T" m. s( L! ]0 O6 R( a% qv.resize( 100 ); // v 已经包含 100 个 0.
/ J/ d. V& ]2 G* r/ b% Xfor( int i = 0; i < 100; ++i )1 a% u$ y* v$ Z& E  e" d2 t! H
    v[i] = i; // 可以赋值
6 [. o% t9 I- u) a; f3 H+ h有时候,一个 vector 可能增长到较多个元素,然后又减少到较少的元素个数,这时,可能希望缩小 vector 分配的空间以节约内存。CArray<> 中提供了 FreeExtra(),但 vector<> 并没有提供相应的函数。这时必须进行复制:
: G4 j9 `5 v/ U6 w$ q9 u' V/ c& y% w8 E. r, M% ]- [  ?
IntVector(v).swap( v );
" E) k+ F( s  \有一种看法认为拷贝构造函数同时也复制了capacity(),而标准中并没有很明确地指出这一点,因此更安全的方法是: l0 L4 Z, B7 d8 o$ ~+ H4 O; \

3 V! z" ?9 [" x0 K7 D! _  \IntVector(v.begin(),v.end()).swap(v);. w/ K5 z. X* J7 B
如果一个 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, 2026-8-13 22:51 , Processed in 0.018790 second(s), 15 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

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