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

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

[复制链接]
发表于 2008-2-26 13:29:18 | 显示全部楼层 |阅读模式
作者:wangtianxing
8 J* F; `& ^. \5 J! f- \+ E1 ^# {* T
' ]9 C" t! R7 M8 }9 f2 v9 o* q原文出处:http://www.cpphelp.net/issue/vector.html
3 z4 @: L6 [( d
7 P1 \/ f, H; V8 A' V$ q# s
6 u7 ?# m# Q1 v7 n5 R, O- C0 S% N, s9 N* @3 l- f& `
摘要: 本文介绍了C++标准库中的容器类vector,分析了它的优点,并且建议在应用程序中使用它作为动态数组的优先选择,而不是MFC的CArray<>等其他类模板。最后介绍了vector的接口和使用时的注意事项。+ r" o9 s$ Y* `: R$ h! R, s+ T1 v$ P

' R$ B6 |! b1 E( \. D! A在一些使用 MFC 的程序中,经常看到许多程序使用 CArray<>,由于 CArray<>的设计问题,造成使用它的代码的复杂化,增加了维护难度。因此建议使用 ::std::vector<> 代替 CArray<>。$ ~; n  G  S2 u7 _) o: V

: `/ X5 y; a" g0 X% W, y4 g另外,也看到一些程序在用 malloc/realloc/free/new[]/delete[] 等手工管理内存。在应用程序中,手工管理内存是容易导致错误的,应该用 ::std::vector<> 之类的对象来管理动态数组。
# k& S/ T2 p8 W' m7 d
& J/ `: f: ]' h8 t4 {" p% s7 V由于 MSDN 中关于 ::std::vector 的内容较少,我们在这里做一些介绍,供参考。5 @/ B/ b! G3 y: J5 T. b! K' O

- F: Z7 g+ H) ~- j! ]% ^- f不熟悉 CArray<>/WIN32 也没关系,这里提到它们的地方并不太多。, B5 K$ L% t$ k0 q

  w- I3 i( p' U% }9 }1. CArray<> VS ::std::vector<> ?
: i' p, S% C0 x/ R- OCArray<> 和 ::std::vector<> 一样,都是模板类,用于管理任意类型的对象的动态数组。都在解构时释放所管理的动态内存。因此都可以用于代替手工动态数组管理。
7 k' h4 E$ A/ O+ d
0 }* b/ Q$ O6 ^; t但是,CArray<> 是在 C++ 标准化之前很多年(VC++2.0时代)设计的,当时对 C++程序设计,面向对象程序设计,模板程序设计等技术认识严重不足,尤其是当时对面向对象技术的错误信仰与宣传,造成 CArray<> 的设计有重大错误。2 y6 {) L0 A7 d
% N5 r( w' [: G: v
在 C++ 语言标准化以后(1998),以及 VC++ 6.0 出世以后,提供了标准的::std::vector<> 模板,基本上在任何方面都要优于 CArray<>。Microsoft 由于要支持老的程序,因此一直保留了 CArray<>,但显然并没有打算按照新的思想去发展它(至少应该提供operator=(CArray const&)吧)。/ O$ {! N3 W" o4 W$ I# L: B3 U

. L; [4 e: P' s概括起来,CArray<> 与 ::std::vector<> 有以下不同:
& V5 a7 g5 h/ f; h
* h, L3 L+ O8 n" B8 s% H9 C1) 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<> 没有继承任何东西,只是实现了管理一个动态数组该做的事。1 w  r* [) M3 B& U6 b7 g9 g; P
) J, j$ x2 b/ b0 V! H1 }$ u
2) CArray<> 不是一个恰当的值类型,例如下列操作都是不合法的:9 G) i% ]7 X+ n% f# F/ R
9 H# a  D  N" r! d5 U
CArray<int,int> a;
1 s! }" f& @$ |; S! _3 e) jCArray<int,int> b(a);  // error, must use Copy().) K: W6 u/ G% x! X7 x! z: w8 c
b = a;        // error, must use Copy().0 a5 H; y4 W5 J0 B; A
b == a;       // error, you must write your own.
2 S& H  {" }6 c+ M' ]b < a;        // error, you must write your own.
5 b6 K4 Q/ j  ^8 X与 CArray<> 相反,::std::vector<> 是一个认真设计的值类型,天生是可以拷贝构造和可赋值的。如果 T 是可比较的,那么 ::std::vector<T> 将自动地是可以比较的。6 E0 G. _: j+ j& x
; a3 H- n: C9 m7 Y9 y
此外,由于涉及到四个特殊成员函数;, s) |3 a4 w# s- Q+ H/ h

9 @+ c% i  J# n- O" k8 _T(); // 缺省构造函数(default constructor)
' W- L' a+ g& a- V4 s  E~T(); // 解构函数(destructor)8 M$ w. U/ F" m  t. s
T( T const& ); // 拷贝构造函数: ]# W* c! \- j
T& operator=( T const& ); // 拷贝赋值函数+ A4 ?' t. N! ~
的自动生成,如果使用 CArray() 作为 T 的成员变量,那么上述的四个特殊函数中的后两个将无法自动生成,需要手工写:
$ o" Q4 c/ z9 @! L7 p4 r9 M5 Y0 B6 g) M, D: P0 m
struct T
) Q: G% ?/ X& S. y+ r{7 D3 p1 w' l& F
   T() {}1 m9 n4 v7 X  v8 a/ k
   T( T const& t )- l- W2 Y# Y- @# Y$ @- @
   {/ S& o5 }$ z* b/ R, S
       a_.Copy( t.a_ );1 X: I/ Y8 Q$ P
       i_ = t.i_;5 Z$ A# i2 ^! f! N
       d_ = t.d_;; i( O" S: `4 {" ^4 h' a
       s_ = t.s_;' ]( h: x" f/ L' }
   }
+ k/ u, t* v" W, _+ j- ]( Z1 ?   T& operator = ( T const& t )+ f* n. p* L! w3 l1 \5 ^
   {
; L4 a% j& s5 {: p       if( this != &t )) @( a+ _. R* y8 u3 S1 S
       {
( w' R2 I' |1 O* k           a_.Copy( t.a_ );  H1 q5 ?5 Z0 J! s, c, a# v
           i_ = t.i_;
3 g. w% g, P; R2 ], ~5 l           d_ = t.d_;  `* q  k" R* P/ k/ S7 r
           s_ = t.s_;
+ a# `8 M. I+ h, `. w8 R2 ]       }9 Q# v- y  {* q# E% j8 K% U1 o3 [
       return *this;
& b6 t( U0 z$ d$ n. w- M   }
! s0 ]9 Q+ i, G* }private:& U$ I7 ~3 Y5 J8 S) o
   CArray<int,int> a_;
& Z# ^% i0 o$ g- Q& c3 f   int i_;
# V. k. s6 I) T4 A   double d_;
/ n7 j' U' M4 c- t5 p9 w& Y   ::std::string s_;
6 @1 g$ R0 N% `1 ^) I8 K};$ O) y1 G6 i% X0 N1 E
如果使用 ::std::vector<>:
5 A- j" C; F$ l  l7 N; r1 b( S5 }7 s
9 b0 z# D3 m, m5 |5 l9 Y- hstruct T
( k! \4 s2 R* P' _0 K7 f7 r{
  A' |3 i9 n9 o: Y: Q/ jprivate:" v  l1 n5 G0 b' U
   ::std::vector<int> a_;
6 E) a2 z6 U( L2 S   int i_;/ X& ]8 a& L. ]0 S9 M8 w- p4 a
   double d_;
  m" }5 d8 x, k) n   ::std::string s_;/ D4 E7 K1 u" a" Z, A( @4 x
};7 o' E& I- {* n0 `  v3 k5 U
上面列出的三个特殊成员函数都不需要写。好处是明显的:当你增减 T 的成员变量时,你不必到# C2 s% J3 K/ }
T(T const&) 和 operator=() 中去相应地增减。' r. Z' U0 N1 O2 I8 `% W
. I2 t' e0 R3 z0 ?
3) 没有现成的算法可以对 CArray<> 进行操作,而标准 C++ 里的标准算法大多都可以直接在' Y1 X+ H: i- c" f$ n
::std::vector<> 上运行。例如:1 Q1 `9 i  Z) A; }. y/ v

5 I" P3 k0 x0 b( Q1 G0 ~  u7 `static int const init_vals[] = { 3, 1, 4, 1, 6, 9 };
8 M4 x. p. m/ G, z2 b6 P$ uvector<int> a( init_vals, init_vals + 6 );, U# x) e8 C& r- N- a9 I% f) w
*find( a.begin(), a.end(), 6 ) = 5;    // 把6改成5
( }0 G! g7 l$ t: S, w. ?! jsort( a.begin(), a.end() );    // 排序。: h" M! s+ d: l# b) ^4 h
可以说,CArray<> 的主要设计错误是把一个本来应该是一个简单的“值”类型的东西设计成一个难用的“对象”类型了。所有的“值”的好特性都丧失了,但那些从CArray<>继承的派生类呢?
6 p" V) ?7 a4 g+ z/ L& h* a- V* S8 i; j) A; v2 T
CByteArray等的问题与 CArray<> 的问题一样,甚至更多(例如,CPtrArray,永远不要用)。2 p& X" u4 _: [1 z6 k$ i

" ?# f( T, O# X  X. I2 v0 O同样,其他的 MFC container 模板,象 CMap<>, CList<> 等,都有类似问题,都应该用
. B" ]) x3 _9 \::std::map<>,::std::list<> 等设计更好的东西代替。* I1 h, N9 i  l7 {8 [( T
& j9 S; p  c3 e0 Z0 A& |
2. ::std::vector<> 在哪里?
" h: Z  j0 x  ?: v2 I::std::vector<> 在头文件 <vector> 中定义:5 f" Y* c2 Z+ V8 p$ m3 X

9 T  Q: B$ o' M" I(注意,标准的 C++ 头文件都没有 .h 后缀,有 .h 的文件是与 C 兼容的,或支持老的不标准的东西,象 <iostream.h>。)0 P4 s$ s0 {$ J* T+ O: u, E

1 a* F9 J, Q# g7 _namespace std
) l; Y) [- F% m7 C{' A. T/ v8 d' _$ J
    template<typename T, typename A = allocator<T> >6 h; L8 [$ a! H) j
    struct vector
- [' }1 A) S: i7 ]/ L7 P' Q! g2 b- s1 w    {9 K; E4 K$ A3 h5 ?; S  K
        // 具体内容稍后讨论+ T* L8 t8 V6 {& i* s
    };
$ B8 Z$ R" I  I. b
; R/ w! ~6 y! X- X7 b8 ]8 n6 z3 N- G5 B2 |# z
    template<typename T, typename A>' |9 p) U' i  c' S8 P. `9 E( n% L+ E8 ^
        bool operator == ( vector<T,A> const& a, vector<T,A> const&    b );; W% T8 B9 g5 ^( N" j  Q+ W) ^# H$ f
    template<typename T, typename A>2 d' A% B: j3 }) A  Z( _: X- }+ [
        bool operator != ( vector<T,A> const& a, vector<T,A> const&    b );7 f  f' S: p! B; m2 o
    template<typename T, typename A>
' @! {/ P! c1 f7 L        bool operator < ( vector<T,A> const& a, vector<T,A> const&    b );1 b- F% K: `. q* y
    template<typename T, typename A>+ l: f9 Y, s3 `/ q' ]
        bool operator >= ( vector<T,A> const& a, vector<T,A> const&    b );- A& f" W8 B# {  p: Q
    template<typename T, typename A>
# Y/ @, c) R/ |# W        bool operator > ( vector<T,A> const& a, vector<T,A> const&    b );
/ d3 C/ ~- P) y& A  x5 E. W( q% o    template<typename T, typename A>4 ^0 I5 N1 _! o9 f
        bool operator >= ( vector<T,A> const& a, vector<T,A> const&    b );& h) b2 j$ J% U# `
}
- S4 S! t( G* @) ?vector<> 定义在 namespace std 中,使用时为了减少击键次数,通常使用一个类型定义缩短类型名称:8 v' s6 t1 B  @4 _0 _* h& \, W: X! a
9 f( e: {3 \. S9 U. ~* r
#include <vector>
6 l4 J5 E; F1 c2 M3 R# H  ?+ L6 Ttypedef ::std::vector<int> IntVector;
6 d/ z5 |0 ^1 j( Q. DIntVector a;3 g% O, ^- ~5 }  v; J
IntVector b( a );
: L$ B6 }% I8 K: z' Z- QIntVector c;
$ s; P4 [! I6 t# g. l6 `' L' w$ Zc = b;8 a4 c0 B9 i5 _7 ?* ~* s
assert( a == c );7 s3 c9 Q. |3 l  q' m* \. A7 M
请注意 <vector> 中定义了六个 vector<T,A> 的比较函数。这些函数只在真的用到时才会被实例化,才会要求 T 也提供 operator==() 和 operator<()。, |8 P8 p* P5 m' p
/ [( d5 ~2 t) N: y7 {7 _$ X
另外,A = alloctor<T>:用于提供一个用户定义的存储管理类。由于这个参数很少用到,而且在 VC++6 的实现中有问题,不能用,因此以下的讨论忽略这一部分的内容。' [% D8 m6 ?+ ], T# {. c

0 }- g& G, e6 W3. ::std::vector<> 中的类型定义& b5 F4 X, m) n5 }& g7 Q
vector<> 中定义了一些类型,下面只列出常用的:4 o1 J; [; Q# g; D& z6 ?$ N

: S( |% p1 ~9 @9 U9 ^; Qtypedef T value_type;
% B% l# x$ ]0 ?5 e* qtypedef T0 iterator;
$ `+ F- ^" U" Z/ C  W* P* htypedef T1 const_iterator;
; k2 `1 E! X3 t4 y9 N7 Ptypedef T2 reverse_iterator;8 {& @( b9 y7 R) z! i0 F
typedef T3 const_reverse_iterator;
$ {8 t, X4 `0 [4 t% Y# i  ?$ r: O7 l6 y$ S* O
value_type 就是 vector<T> 的元素类型,也就是 T。当写通用的算法处理任意类型的 vector<> 或其他容器类型时是很有用的。
- `9 a! H( }- R6 q/ x
( m) i8 y+ K% Y$ A* ?$ biterator/const_iterator 是两个 vector<> 的实现定义的未知类型,用于访问vector<> 中的元素,类似于 T*/T const* 指针,他们的区别是一个指向的元素可被修改,另一个只可以读:
6 X$ @: P" x# i
+ `% _7 A5 y& h& [1 Jtypedef ::std::vector<int> IntVector;: w/ _/ G" J) c  C" a- c2 ]1 j
IntVector::iterator iter;0 |6 \5 }$ s3 I( s8 b$ j
IntVector::const_iterator c_iter;. s! n- i9 O) ^* z6 r" x
// ...
6 u2 O% W* U5 y$ g4 n3 j$ A, M" ^++iter; iter++; // ok: increment, post-increment.7 A! |/ ~: v5 G
--iter; iter--; // ok: decrement, post-decrement.% g# c7 a4 s/ A! W
++c_iter; c_iter++; // ok: increment, post-increment.2 ?0 H/ u: k3 Y, r- k
--c_iter; c_iter--; // ok: decrement, post-decrement.9 L/ E& S$ H0 o* f  x0 M) s
*iter = 123; // ok.
: j7 n$ W6 F0 i2 H( Aint k = *iter; // ok.
+ b9 H* A$ p) M4 I8 }# N8 Tk = *--c_iter; // ok.2 T. {: [% x" r4 ]/ I* H
*c_iter = k; // error.
3 o" I; ]8 w0 G+ Z9 d4 {4 Hc_iter = iter; // ok: iterator is convertible to const_iterator.
" z% g. v. P: d" witer = c_iter; // error: can't convert const_iterator to iterator.; c" M5 N% l/ O
在使用上 iterator/const_iterator 和 T*/T const* 基本相同,事实上有些vector<> 的实现里就是用 T*/T const* 实现 iterator/const_iterator 的,但又不可以把 iterator/const_iterator 当作真正的 T*/T const*:) ]- N7 C$ v/ d

% R# t+ s3 v% A7 u- b! @T* p = iter; // may fail to compile.
" e: ~& J9 X4 TT const* q = c_iter; // may fail to compile.
7 x3 i) x) I: X; [: d+ ereverse_iterator/const_reverse_iterator 与 iterator/const_iterator 类似,但以相反的次序(从尾至头)访问 vector 中的元素。8 k8 e/ _) e; c& A: ?. T2 B/ m
$ v. }; w2 \  H  @' R; b# \
各种各样的 iterator 在 STL 中有特别重要的意义,但这里我们不做具体介绍。只要理解通过 iterator 可以访问 vector 中的元素,大概相当于一个指示位置的指针就行了。) F3 J2 }: }# m- h5 ?# o- t
) g! E0 t( |5 b- C3 k0 Q; G5 g
4. ::std::vector<> 的构造
; s6 Y" V  w% r* q4 hvector<> 提供了以下构造函数:(忽略 allocator 参数)) L) l3 h. H" r$ L

$ ?1 g" `9 _% J* ^0 o0 avector();9 ~& Z7 ~, A) {: r( M
vector( size_t n, T const t=T() );& c3 ?! u, I+ H
vector( vector const & );1 M* V2 \! l! ~" p) G6 G( i
vector( const_iterator first, const_iterator last );% ~6 e" ?8 L" K& {; F& V' Q9 Q* u
1) vector();: G$ t* Q* f1 v' Q6 O& `% o& Z/ J' T
+ u5 R" z! V! ~: }- b( f
构造一个空的 vector,不包含任何元素。
1 [. W, `0 @' Y( B1 ^3 t8 Z; v$ _0 o5 Z- _( U  t
IntVector v1; // 空的整数向量。
' R+ m7 ^# K$ ~  X# K2) vector( size_t n, T const t=T() );
) P, k/ R5 h, j1 u
& l* {" T  i6 s/ D; a1 V# `4 q( h" Q构造一个 n 个相同元素 t 组成的 vector。如果不给出 t,那么将用 T() 做缺省值:
3 s7 r4 [( K/ I' H0 I" s1 G, F3 W% d) O: Q# m2 z- _/ g, U
IntVector v2( 100, 1234 ); // 100 个 1234.* M1 n$ Q+ F6 m4 d& K  w
IntVector v3( 100 ); // 100 个 0。
# z" B# N( K- X. \5 c+ `2 r3) vector( vector const& other );' d4 d+ [, z  g) C+ v6 k

% ~/ p; }1 U6 D3 w复制构造函数,复制 other 中的内容:
) |6 t' z1 E7 ^% `# _
( s! u, \, u. M5 k% d) QIntVector v4( v2 ); // 100 个 1234。1 r  l7 \' c  V% L" C# T: i) o
4) vector( const_iterator first, const_iterator last );
( x: `4 _3 N5 T. Z  ?9 D! Q
9 ^  @% b, P1 b7 }6 x* E" Y, O事实上,这个构造函数应该为% Q8 a# B( X/ v2 e, H

6 r! g3 R4 v0 H: a7 p4 Q$ Xtemplate<typename Iter>
  h  v. H  ?; X0 S% Q    vector( Iter first, Iter last );
: z3 N4 r$ A+ j, J  e6 Z  @1 ]即拷贝任意的序列 [first,last) 到 vector 中。由于 VC++6sp0 编译程序的限制, Iter 被换为 const_iterator 了。不过,碰巧 const_iterator就是 T const*,所以可以如下使用:0 u- _( i3 x! w7 U  G* H
4 e# t3 F! z( x& Z8 V# h( B' T
int a[] = { 1, 2, 3, 4, 5 };
1 S& x" {5 Z( O+ A) d* g& n3 KIntVector v5( a, a + 5 ); // {1,2,3,4,5}
, T+ S) c. a$ m: H; J, k( j" E+ SIntVector v6( v5.begin() + 2, v5.end() ); // {3,4,5}
" I$ o/ {' e$ ]) E" S/ b5. 访问 vector<> 中的元素2 x# Z: P. m9 C$ D  @
以下成员函数/运算符用于访问 vector 中的一个元素:' t" [. ?* j1 V$ S& _/ a

" |: }2 U% V" ]6 J# aT& at( size_t n );' S& r( M9 l: x
T const& at( size_t n ) const;$ m1 e5 S( b' B( v$ A) ]
T& operator [] ( size_t n );
8 X- i: \% J/ \, CT const& operator [] ( size_t n ) const;9 P2 w% B# W. j
T& front();
8 A+ t4 M( R4 J  pT const& front() const;& A) ]0 |& O6 `  J; w* x* v
T& back();
! F8 Z# h4 f2 N2 q1 W1 C( O( n. ~T const& back() const;
, @) `: v- i2 o' C8 q请注意,由于 vector 是一个“值”语义的对象,所有的操作函数都必须严格保证 const 的正确性。所以,所有的元素访问方法都有 const 和非 const两个版本。
, B9 s. t  s% y$ r$ i& v( j# U" v8 F' ]) |* b8 w5 n4 Q
at(n) 和 operator [] (n) 都返回下标为 n 的元素的引用,他们的区别是,at() 进行下标越界检查,若发现越界,抛出 range_error 异常,operator[]不进行下标检查。
8 b; l" I, ~3 X# b/ j4 a$ u/ x0 G8 H& U; [% n
front() 返回下标为 0 的元素的引用,back() 返回最后一个元素的引用。
- x1 ^. f1 O4 [* o1 C. C& n1 ?* |. P1 Y; c$ d2 S0 e
int a[] = { 4, 1, 4, 1, 5, 8 };
3 D( i+ x. Y6 I) YIntVector v( a, a + 6 );
" D! m5 q9 J, D* ^4 Y' j// 使用 front(), back():
; ~- {; f3 i! vv.front() = 3;7 o" A/ A7 P& f7 R6 h! p& ]
v.back() = 9;4 E. S5 p, [. ^( Y
// 使用 operator [] ():
+ D% t! _" P0 k$ T/ }" o% xfor( size_t i = 0; i < v.size(); ++i )
  A* X' ?0 H0 J! k::std::cout << v[i] << '\n';5 l5 o" z6 W1 g; K" ~$ G+ H5 y
6. ::std::vector<> 的存储管理
* K) D% r; F2 z$ p8 n以下成员函数用于存储管理:
  z  h8 z% O% X! z3 c, Z" ^! R1 V7 d3 L  o* V1 g7 ~) F% {: M
void reserve( size_t n );+ ^' p% ?! E. J2 L( c
size_t capacity() const;
3 j, [  ~7 Q0 Gvoid resize( size_t n, T t=T() );$ d: p1 k7 y* B/ M9 }
void clear();
" a$ v3 A7 q( y; w5 ]* N3 ssize_t size() const;
9 _$ E9 P) u/ `5 ~& ?% Vbool empty() const { return size() == 0; }2 n5 k2 L0 Q- F4 g/ @3 W* a2 ~% p
size_t max_size() const;# F: ]' l8 F9 M- z0 A/ a  d$ F

" H# s1 y4 L* r5 n/ H4 O另外,push_back(), insert() 等也涉及到存储管理,后面另行介绍。( S" \( h3 r. m+ ]5 J- N4 k) b
( G& K, l1 p) V4 U6 u
1) max_size()& J9 X7 U8 w1 M# T, w8 k! y9 M
$ f  z& d: }, B. o' A
返回 vector<T> 理论上可以装的最多 T 的个数。这只是一个理论上的数字, 大概是 4GB/sizeof(T),没有多大实用价值。在程序中不要用。4 N3 j6 O/ ^. w; `. ]) S
6 d* I% V6 K( e( b7 B& l
2) size()
: @+ a$ Y9 {! v- g- @/ P/ ]# d9 _& e3 E. m* ]8 C
返回 vector<T> 中实际装的 T 的个数。相当于 CArray<>::GetSize()。
; N7 P! @) I3 _2 j+ c% [, j4 p5 x5 Q$ C
3) empty()
! @- s; O, \( K6 @1 r; z% Z- C/ j# z& i
如果 vector<T> 中没有任何 T 对象,返回 true。也就是返回 size() == 0。
. c+ w1 h0 W* N8 ]8 a& b9 i2 k$ n; e) @2 z
4) clear();4 ]& G5 y  v' O5 _; f$ q

. o$ y3 t3 r& p+ C清除 vector<T> 中的所有 T 对象。执行后 empty() 返回 true。大致相当于 resize(0),但不要求 T 可被缺省构造。相当于 CArray<>::RemoveAll()。
4 B  s. F% [( `/ V* a
5 B( }# L2 O  ^5) resize( size_t n, T t = T() );& o' g' H3 j* D# r' y

3 I7 h) m% |, U$ o4 Z7 Y将 vector 中的元素个数设置为 n,n 可以大于 size() 也可以小于 size。如果 n 小于 size(),那么 vector 中下标为 n..size()-1 的元素都将被解构。如果 n > size(),那么将在 vector 的后面新增加, I" q# _7 x- ~
n - size() 个相同的元素 t。在增大 vector 时,可能发生存储再次分配。总之,调用resize( n, t ) 后,(size() == n) 成立。
# P, \/ ^: u% f1 ~% M, i3 T" U# V# [" Z, q
请注意,如果调用 resize( n ) 不带参数 t ,那么 T 必须可以缺省构造。  Q! W" l/ N% I7 ?; G

5 ]2 U/ c+ Q+ z; U  b- w) z" `6) reserve( size_t n );9 H/ Y" N# K$ l2 ~: w6 X
# L* E& K$ U7 B$ _% k% j9 L4 k
事先分配至少可以保存 n 个 T 对象的空间。调用后 (capacity() >= n)成立。% ]' Q0 ~8 r9 Y" r

8 w: I- R0 }; c' P5 \( p" n) v; _% K7) capacity();/ s# |$ e  J# P3 c9 Q+ y

# k) f, N; b4 ^5 v2 a% R返回已经分配的存储空间够容纳的 T 类型对象的个数。后续的增加元素操作(如 push_back(), insert())如果增加元素后 vector 中的总元素个数不超过 capacity(),那么 vector 的实现保证不重新分配存储空间。
% ]+ ~- C* e5 q4 u( R! }0 c5 Y, \8 z: p
: d5 O0 s7 R9 y* A: S+ E% Cvector 管理的动态存储空间是连续的。执行操作& F/ _- @1 B5 B4 P
* x& @0 H4 o1 V7 a
IntVector v(7, 1); // seven ones.5 w7 k  ?7 [* X! S2 d! I
v.reserve( 12 );0 f- E" d2 [8 v8 n0 B8 \' F- M
后,v 的状态可以用下图表示:" ]; e/ \% E9 s# H) m9 p
7 t8 F' X+ J% h. ^- C
/--size()---\% b( D+ y; E( T( b
|1|1|1|1|1|1|1|-|-|-|-|-|; @+ Z. [( u6 @/ r  t) x
\--capacity()---------/
  O' U  K& }' u1 t/ H其中,1 是已经构造的 int 类型的对象,- 是可以构造一个 int 类型的对象,但还没有构造的原始空间。再执行
+ b  ~; B+ l0 v- g: \7 C. R0 s. U( d( ]" `
v.push_back( 2 );; {5 e% B7 e2 X+ E, J- m
v.push_back( 3 );$ {; B( D2 N9 N  z- p9 n
后,v 的状态可用下图表示:
9 Z5 ?5 C, {% h) c$ T- j% R& ?4 {) T5 O9 [$ [- x# W0 l1 X. N% h% X
/----size()-----\
  [, ]. p& W0 C) @4 _|1|1|1|1|1|1|1|2|3|-|-|-|& ^! C. ^+ e3 k$ C" [2 v
\----capacity()-------/5 v+ N' W( j$ r  T1 N  r
执行 resize( 11, 4 ); 后:
6 c  m/ v! o" s) l8 h( p( w; f! L7 m9 Y% }- Y3 P5 L
/----size()---------\
3 j. G4 [( ]" Y|1|1|1|1|1|1|1|2|3|4|4|-|$ {- ]# ?5 O. i. i% {
\----capacity()-------/2 \! t4 I. c0 Z" Z6 U
capacity() >= size() 总是成立的。对于下标为 [size()..capacity()-1]的未构造对象的存储空间,是不可以访问的:
/ a6 C- d. f! S- \3 g0 C. v+ z* g# g% m, X3 H" Y
v[11] = 5; // undefined behavior - anything can happen.+ E8 y5 f, q% x' ?1 D
7. 添加元素到 vector 中
2 t1 |$ U' \: ~2 I  `+ k- z下列操作添加元素到 vector 中,并可能引起存储分配:
& d, k/ Q" u2 B
. O& v5 r( ]- X- V5 i6 Lvoid push_back( T const& t );
* @* U' c- m' X; k6 g& t  Gvoid insert( iterator pos, T const& t=T() );- S6 I- b9 ~% ~" N
void insert( iterator pos, size_t n, T const& t );5 ^! e- C- Z- s7 ^
template<typename Iter>
) V9 g! t0 f6 u8 t: B    void insert( iterator pos, Iter first, Iter last );
) J. A; X- b: Y) x3 u. Opush_back() 是把一个元素添加到 vector 的末尾。insert() 是把一个 t,或 n 个 t,或从 first 开始到 last 结束的一个序列插入到 pos 指示的位置之前。
- b8 _- G1 Z- I& Z) v; s
1 B3 I9 I/ `/ D2 |! f; s) w, [; ?当插入元素后 size() 将会大于 capacity() 时,将引起自动存储分配。vector 将会分配一个比需要的存储区大若干倍(通常是1.5到2)的新的存储区,把老的元素拷贝过去,同时完成添加或插入,然后释放老的存储区。5 d# z5 K) ]! {: d

, e4 M6 `( y$ X) y+ y8 E4 D7 S这就是说,vector 自动存储分配的空间大小是指数式增长的,这可以保证多次添加元素到 vector 中时,平均用时是接近于常数的。! }  q# O' @6 B6 z$ ~

' D$ k% V' i3 s, \& i( lIntVector v;
, o' q, y0 i. f+ k   5 `& x0 B3 }- D! Z. m1 G
// add 0, 1, ..., 99 to v:, r0 y7 v  n; C+ V4 b7 m1 U7 E
for( int i = 0; i < 100; ++i )
1 L+ z8 B+ t. Mv.push_back( i );
2 Q2 y0 O/ G5 L) {; `   
" I0 ^& m. T% g$ b2 j// append 9, 8, 7,..., 0 to the end:
! Q3 q- U' d- q+ v9 Z: i- v. jint a[] = { 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 };
, x2 ?4 T. L1 n, zv.insert( v.end(), a, a + 10 );+ V! t+ y$ B1 d: p- {# b* s7 i: ]
8. 删除元素! d$ M  b' N, L. C' ~, V  F2 [
下列成员函数完成元素删除:0 e' F% |* ]; l" R5 g2 o% E* i
) |4 U* Q$ A# a1 J5 |$ a# M. g
void erase( iterator );: t* |- P$ p  w3 B6 p
void erase( iterator first, iterator last );7 T: }9 I" M3 k, `5 E% L
void pop_back();
9 M0 q- d# X1 I/ yvoid clear();
2 ^7 f3 x0 D; u' V$ ?6 _8 K) K9 l这些函数分别删除一个,一串,最后一个,或全部元素。
1 Z5 R1 U1 l5 d7 \. e  I. @5 L, @* {8 i
IntVector v;- B2 K" G( `3 w4 V( X
for( int i = 0; i < 100; ++i ), x3 B% h- X* m3 N* e% }
    v.push_back( i );
* [: n8 h- {4 V. \  U   ; o+ X3 D: a! ~
// 删除 50, 51, ..., 89:) ?% \! z/ v/ t' p) e6 f
v.erase( v.begin() + 50, v.end() - 10 );7 n! _% V2 S: |# C% J7 p7 R# u
   $ x5 q6 V$ o1 N# E9 a# H. T# r
// 删除 49, 48:
% l, j5 O3 j' R1 Y" vv.pop_back();, H2 F7 L) P$ U8 r+ t8 r, j; e( K1 p
v.pop_back();" W0 i' h/ ~' }: j* e- n2 K8 y
   
$ m5 e" X8 q. A4 n, g' a// 全部删除:; L$ b! ?7 T8 A3 c
v.clear();- n$ _  }# m: W7 _  D
注意,删除操作不会引起存储分配,因此 capacity() 不变。6 I1 U3 R, o% V& o& [

. W( a3 F3 y2 i- j9 K5 y5 l9. 作为序列访问 vector 中的元素% h2 D& K' y2 f% `- I/ r0 f& B
序列(sequence)在 STL 中是一个非常重要的概念,所有的容器类型和算法都涉及到,而且所有的算法都是建立在“序列”这个概念之上的。, q( s* H6 H! d( c

# ]1 k. E$ q5 M; Y& v“序列”是一个线性结构,由一个指示其起始和一个指示结束的叠代子(iterator)来决定。如果 first 和 last 是某种类型的叠代子,那么经常用[first, last) 来表示一个序列。注意,first 指向的元素是这个序列的一个元素,而 last 指示的是这个序列最后一个元素之后的位置,可能根本没有元素可以访问。这种半闭半开的区间表示是整个 C++ 标准中的约定,而且确实可以简化程序。
/ \% x' j+ T. ~3 K) e8 v  f3 m( i. E2 O* o2 A
叠代子是传统的 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 的要求。* m, \7 ]1 I3 m! J

# F" N- f9 `5 e! L3 Y+ r. z8 Cvector<> 中定义了以下函数用于获取被控制(管理的)序列(动态数组)的各种叠代子:" L, k7 Q1 T' r, M; g

. P3 T- V: p" A1 k8 Miterator begin();5 I, M1 U1 X# X& Z: l8 x
iterator end();
9 L( m4 S6 S4 Z1 B# tconst_iterator begin() const;
( |) b6 P" c0 N0 H1 P4 ~9 pconst_iterator end() const;8 m! t4 e! i7 [" @0 ~- A1 V  x6 R
reverse_iterator rbegin();
( }( a( A/ {3 E+ r' Y. ^reverse_iterator rend();" u* u. n/ w) p9 Z+ T
const_reverse_iterator rbegin() const;
( s+ B+ B' v1 V; s; @5 gconst_reverse_iterator rend() const;" w2 V% \% c( Q. a
这里我们不讨论叠代子的一般概念,只举几个 random access iterator 的例子:
4 V# D0 k& }" `7 A. H; t+ A+ J( f
8 X% o4 O3 T$ T$ X/ Dint a[] = { 1, 2, 3, 4, 5, 6 };
6 r+ f* ]) L4 |; I8 l[a, a + 6) 是一个随机访问序列,指示了 a[] 中的所有元素。这里叠代子的类型为 int*。
2 I9 u/ C' T, ~  u$ Q  ]8 R
8 V2 x+ c' H! ]: h6 y+ B[a + 2, a + 4) 也是一个序列,指示了 a[] 中的 3, 4 两个元素。叠代子的类型仍然是 int*。/ d9 I! }0 [6 k& L$ Z4 ~( ^

( B1 l0 w7 q$ D, y' QIntVector v( 100, 1 ); // 100 个 1。# i0 n7 h2 ]& \  A; P1 F% N
[v.begin(), v.end()) 是一个随机访问序列,指示了 v 中的所有元素,叠代子的类型是 IntVector::iterator。5 y+ @, ]4 N7 G+ U. v

, t* I: e5 f$ N3 s" ][v.begin() + 10, v.end() - 20 ) 也是一个随机访问序列,指的是 v 中除了头 10 个和尾 20 个元素外的其它元素。6 Z9 t4 V0 I% d5 b8 R9 ^( z& X

9 T$ v0 O2 P2 L/ Y/ `[v.rbegin(), v.rend() ) 是一个随机访问序列,指的是 v 中的所有元素,但与 [v.begin(), v.end() ) 不同,这个序列是从尾到头遍历所有元素。
2 ^0 I( w7 q& {% ~/ L
" H2 K  @: |- j- R/ n[v.rbegin() + 20, v.rend() - 10) 与 [v.begin() + 10, v.end() - 20 )指示的元素相同,但遍历顺序相反。/ c4 {/ y- D$ A6 s2 m$ @7 o
, T$ u( u0 A" }6 d9 l% ]6 x
下图是有十个元素的 vector 的 begin()/end()/rbegin()/end() 的示意:
, u  C4 I' H3 E6 T; R
0 f/ y' z' u6 G0 u- _begin() ----------> end()
% T" g% h) Z9 x# Z5 D+ ?7 `  |                   |
4 a8 E% ~5 z" M2 O- c: x. h: V$ [" B  v                   v
& }1 {; S, \7 q+ w  v |0|1|2|3|4|5|6|7|8|9|
* P: D8 B/ M  D^                   ^. l: @2 i5 g" Q
|                   |/ f/ G6 }% v1 N* o; G
rend() <---------- rbegin()
4 t) d. O8 T6 y* b8 D   
' g9 i7 J) p3 k. ZIntVector v;3 o5 ~. k, C5 u( c" W. `0 H0 z
for( int i = 0; i < 10; ++i )
, @( n+ x2 N# {! }v.push_back( i );
; e/ l" ]% p& X" c: t  V" ~$ M$ {   
  H% J& ?' v: S& `3 l2 d( @9 f% `! y// print 0, 1, 2, ..., 9:' p& H2 ]' T5 z) Q( L7 U
for( IntVector::iterator i = v.begin(); i != v.end(); ++i )
; z, T% D7 f+ C& u) u/ M, r::std::cout << *i << '\n';0 W. o9 S6 v9 a9 D
   
3 q. k6 r3 N0 u8 z// print 9, 8, ..., 0:, y6 \: a: E0 @+ g' M
for( IntVector::reverse_iterator i = v.rbegin(); i != v.rend(); ++i )/ I! V* ]- ^# v4 v5 B
::std::cout << *i << '\n';
" U4 ?* v6 U0 X! E8 b: x" a除了使用 begin()/end()/rbegin()/rend() 来遍历 vector 中的元素外,由于 vector 管理的空间是连续的,因此可以直接取地址进行处理:
* X$ @6 g- w  [0 E. @" j, w
0 ?$ D$ D- ^; c6 @% W1 }3 y( o::std::vector<HANDLE> handles;
, {$ f* B; x- o: Phandles.push_back( handle1 );. y& X+ ^, P1 g: t5 J
handles.push_back( handle2 );8 d4 m) d, g+ L( K: M" q# O% c' E
5 r4 D( D8 M7 i& b
- S. w) v3 ]+ j9 d0 N
::WaitForMultipleObjects(handles.size(), &handles[0],TRUE, INFINITE);
) V, W, B1 |, [& O  ~这在与 C 库函数接口时尤其有用。
. v* s; F9 g# B
9 n2 }+ @8 `, q. ^10. 赋值和交换3 z7 x7 O9 t; Y; g+ g* L
vector<> 是可以赋值的,这也是一般的“值”类型必须提供的操作:' U4 q( M) U: h& S$ P

$ p% @2 W8 I+ \IntVector v( 100, 123 );" i3 _! Q9 d+ |  C" |3 S
IntVector v1;
/ Z5 u" q  E% d2 W0 wv1 = v;
9 m$ p( \" C: o) qvector 另外还提供了
! O% b' p* h8 j1 A# n( m$ q6 X7 t
+ c/ i& j& L) K; ~2 f2 L& wtemplate<typename Iter>
7 @, [2 G$ N0 J# p! y  ~  Vvoid assign( Iter first, Iter last );
& ?1 d, S# `: S4 B. V, k7 Qvoid assign( size_t n, T const& t = T() );
: \1 l& W/ E' E, y用于赋值:
$ F; {" @3 Q, O) `: T3 J. j, z( N8 R5 _* ^9 `
int a[] = { 1, 3, 5, 7 };
& `. e; \+ B3 y- k, Q% m* Pv.assign( a, a + 4 ); // v 将包含 1, 3, 5, 7.: @+ V" i7 a: x5 ~* \, N+ @
v.assign( 100 ); // 100 个 0。
) Z: t7 O) I( Z& P2 f3 R还有一个很重要的操作:, A) M( U9 p3 ]3 T6 B& g8 r
1 h6 `( |1 y2 z1 }' [
void swap( vector& v ) throw();2 H, p0 [0 V% I/ k% [+ k- V
用于交换两个同类型的 vector 的值。它的特点是快速(只需要交换内部的三个指针),不产生异常。这在写一些保证异常安全的程序时非常有用。
) @/ B# @1 b( K4 P+ E. M7 f* X# Q. l' h5 ?* @+ a/ T
事实上,swap() 基本上已经被当作类似于 operator=() 的一个“值”类型应该提供的基本操作,::std::swap() 也应该为用户定义的类型进行特例化,调用相应的类的成员 swap() 函数:# j4 {/ `' Z0 V

. q4 Q/ m8 {2 W1 u3 {- \! ]) \struct MyVal
* S5 I4 M& T7 {6 e{# @9 e' D" A# A& ^6 w. K3 n2 k
  // blah blah.
( @* E5 P2 \+ z4 f, w  void swap( MyVal& ) throw();# a7 g+ [8 S  {
};5 ]) k# k# J3 Y0 O
   
" ?& l/ c4 a5 B; S8 T& z* bnamespace std {" `( ^. n; J& h$ M
  template<>
9 J5 O4 z% O. M! D5 j5 u    void swap( MyVal& a, MyVal& b )  @6 p. X0 J" A' _
    { a.swap( b ); }
) f2 B1 u! x# ?/ g$ j}- w. |( w6 c6 ?" W7 D
关于 swap(),值得专文讨论。这里我们只指出,vector<T>::swap() 是快速的,不抛出异常的,很有价值。7 p0 O1 p  w8 d- \
3 o6 n  p3 x1 c# U) C
11. 使用 vector 时的存储管理策略
( \. g4 p$ w( e2 _, k" q! i* r从前面的介绍中可以看到,vector 的自动存储分配是指数式的增加存储空间,而且永不缩小已经分配的空间。这在大多数情况下是合适的。 如果应用程序事先知道要用到的元素个数,可以先调用 reserve() 来保留(分配)空间,这样可以避免以后增加元素时不必要的重新分配和元素拷贝:* S" J* f3 M6 x. q

) ]3 \) p& v# C& \# qIntVector v;
; ?. _' D) o, s. P/ ]+ \$ m6 uv.reserve( 100 );% S; s, n) _8 u  |3 i6 w0 I
for( int i = 0; i < 100; ++i )5 j6 y3 w1 J* Z  Y& }. b6 a% I
    v.push_back( i );
$ Z& {$ E5 o2 r. l请注意,reserve() 和 resize() 是本质上完全不同的。reserve(n) 保留的是未使用而能够使用的原始空间,而 resize(n) 是真的创建了 n 个对象:
5 \! Q* u$ S/ q$ l
5 C, a; D2 H4 `5 T/ Y; V1 sIntVector v;$ A6 e) Z( k; q$ H" S
v.resize( 100 ); // v 已经包含 100 个 0." V1 @" G; e4 F. U
for( int i = 0; i < 100; ++i )
$ w0 x6 [# z2 W2 F    v[i] = i; // 可以赋值
. [# I) \- ~0 ~有时候,一个 vector 可能增长到较多个元素,然后又减少到较少的元素个数,这时,可能希望缩小 vector 分配的空间以节约内存。CArray<> 中提供了 FreeExtra(),但 vector<> 并没有提供相应的函数。这时必须进行复制:& k7 Z! o% ?0 ~: t: V

. A& g, K$ i: lIntVector(v).swap( v );" I  f' n9 k6 b' m, n+ \  Z
有一种看法认为拷贝构造函数同时也复制了capacity(),而标准中并没有很明确地指出这一点,因此更安全的方法是, \2 o, L5 i  A, R( l

' {  J; B" o& n0 v2 E  L- [IntVector(v.begin(),v.end()).swap(v);
, I. L1 `" n; j" n" ?* d2 M如果一个 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-6-20 00:06 , Processed in 0.036929 second(s), 15 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

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