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

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

[复制链接]
发表于 2008-2-26 13:29:18 | 显示全部楼层 |阅读模式
作者:wangtianxing5 ?7 k  f' r4 p! P9 q7 F
6 _: z; {( _5 e1 e" J+ T2 f
原文出处:http://www.cpphelp.net/issue/vector.html% C6 Z0 y8 \% U

  ~& k# H5 n8 y8 a; s
& e) M" c3 u. _6 M
! t. `' q9 R) |# J: v7 s. r摘要: 本文介绍了C++标准库中的容器类vector,分析了它的优点,并且建议在应用程序中使用它作为动态数组的优先选择,而不是MFC的CArray<>等其他类模板。最后介绍了vector的接口和使用时的注意事项。. u$ m2 n7 h- v8 u9 Z" S

! B, B) p: T8 X* v9 h在一些使用 MFC 的程序中,经常看到许多程序使用 CArray<>,由于 CArray<>的设计问题,造成使用它的代码的复杂化,增加了维护难度。因此建议使用 ::std::vector<> 代替 CArray<>。
3 [. V9 }( \% ^) W) j+ X5 ]% @4 @9 i8 c, Y; o" I) z( n7 t
另外,也看到一些程序在用 malloc/realloc/free/new[]/delete[] 等手工管理内存。在应用程序中,手工管理内存是容易导致错误的,应该用 ::std::vector<> 之类的对象来管理动态数组。
/ z5 E. D  j( p( G! P* ?3 b, Z( \! ^" y) M
由于 MSDN 中关于 ::std::vector 的内容较少,我们在这里做一些介绍,供参考。# }/ E8 |& X% Z5 p' ~" U

6 t7 J) l9 W! E3 P不熟悉 CArray<>/WIN32 也没关系,这里提到它们的地方并不太多。* S) v8 K4 h$ R4 z7 W0 p. Y
9 h8 |: ?  s% a' G' ]: n8 N& \
1. CArray<> VS ::std::vector<> ?# [& H! k+ g  q2 i1 g4 d! f  N
CArray<> 和 ::std::vector<> 一样,都是模板类,用于管理任意类型的对象的动态数组。都在解构时释放所管理的动态内存。因此都可以用于代替手工动态数组管理。2 B$ ]% w2 P- `% t. h8 }
5 }& M- Q5 @6 E6 q
但是,CArray<> 是在 C++ 标准化之前很多年(VC++2.0时代)设计的,当时对 C++程序设计,面向对象程序设计,模板程序设计等技术认识严重不足,尤其是当时对面向对象技术的错误信仰与宣传,造成 CArray<> 的设计有重大错误。
: q; B$ n5 B4 a
. @' J, P" G% R* e! k在 C++ 语言标准化以后(1998),以及 VC++ 6.0 出世以后,提供了标准的::std::vector<> 模板,基本上在任何方面都要优于 CArray<>。Microsoft 由于要支持老的程序,因此一直保留了 CArray<>,但显然并没有打算按照新的思想去发展它(至少应该提供operator=(CArray const&)吧)。5 y& q# v8 s5 t, r
* q( c; R4 k# a1 y0 O* o
概括起来,CArray<> 与 ::std::vector<> 有以下不同:+ R; V0 R# }- P6 @; h" I% ?

& F7 x7 K5 W+ L+ w1) 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<> 没有继承任何东西,只是实现了管理一个动态数组该做的事。
% _7 o6 B4 s, B! }6 I% r" q* T6 V1 \: ~% e- j/ v: k
2) CArray<> 不是一个恰当的值类型,例如下列操作都是不合法的:2 A! Y; B; T, g, G: D& i! i
# i. k' J9 \) A- |" W. ?9 o6 j
CArray<int,int> a;
9 u" n/ p. O3 m" x9 b# S2 t6 a: F# KCArray<int,int> b(a);  // error, must use Copy().1 j- h: Z2 X" ]$ M. j  b
b = a;        // error, must use Copy().
1 l! x/ F' |+ K7 F3 l% }b == a;       // error, you must write your own.
1 L$ [8 B! s4 R% {4 J- _+ T; V5 a; Xb < a;        // error, you must write your own.
" _9 v2 C. l, i# b( r与 CArray<> 相反,::std::vector<> 是一个认真设计的值类型,天生是可以拷贝构造和可赋值的。如果 T 是可比较的,那么 ::std::vector<T> 将自动地是可以比较的。! }: k8 E6 h5 E: k0 P+ c
5 V# t2 ^6 ]" u7 y' U7 H
此外,由于涉及到四个特殊成员函数;
8 }7 T: b) m8 P9 Y$ B) d& g, j: l& i# B8 w
T(); // 缺省构造函数(default constructor)
1 d7 v5 X) ~' o: T~T(); // 解构函数(destructor)% Y4 K* b7 d2 B& U
T( T const& ); // 拷贝构造函数% S8 a+ }# k3 @' Q1 V% x
T& operator=( T const& ); // 拷贝赋值函数1 ^' y1 I' S1 M% S, M
的自动生成,如果使用 CArray() 作为 T 的成员变量,那么上述的四个特殊函数中的后两个将无法自动生成,需要手工写:$ @9 `0 H; k, W) B$ }' {

& C6 {, _: ~* |5 s% ~  G8 _( ^2 S struct T
% j7 W) [; B& a$ P{
* H/ p* [5 C9 W6 R4 K3 q   T() {}
( \! k! i( n3 t5 V' ~3 L   T( T const& t )" [! C! ^, X. V  a* m+ |  A/ C9 c
   {
  e! d2 @  L4 P7 P       a_.Copy( t.a_ );- @3 y9 S5 V6 L/ c9 B9 h- d) j8 c$ N
       i_ = t.i_;
; y; p; D5 C" V$ Y9 l       d_ = t.d_;2 i. A8 G% ?# J
       s_ = t.s_;
- n# U! J- A1 h0 T" B% p   }
+ j- O  U; o9 k5 M& o   T& operator = ( T const& t )
+ u8 R2 e# U. D8 Q$ P/ }   {8 }6 M- {$ ~9 `
       if( this != &t )
% [0 D% i% C" I% j* m! W; v       {
+ f- l/ B0 i9 ]- l" p           a_.Copy( t.a_ );( @" C) r- _; j/ Z! V
           i_ = t.i_;
' b% h! R0 Q9 ?$ X           d_ = t.d_;+ u$ W9 a  Y) ^/ U% o( G
           s_ = t.s_;6 `5 a7 I5 \2 F( V+ m/ V2 W
       }
- D( g$ Q1 d: ?- _' _9 b1 x  Y: P       return *this;
. P+ @* H8 y" f$ t, k$ Z* c$ ?   }
4 M9 b2 ~% V5 g( E) d- tprivate:
; D" |4 \6 m; I0 Q/ v4 v$ |' c   CArray<int,int> a_;: X! q. B& j% U5 D0 o* y# W( @
   int i_;
( S! {9 e2 {# V- {+ g' @+ y   double d_;) i6 j0 g& K6 F7 {9 z
   ::std::string s_;
- n8 k% F3 \$ q! J};! ]4 K8 E0 ~+ W0 n$ g7 K
如果使用 ::std::vector<>:6 i8 O9 h) o" U4 K: A

. f; h1 V0 D# l) |* J: q5 astruct T
- n- R4 W; ^# _. m' c9 J{
* l5 ~% I; C5 U9 @9 ^$ pprivate:6 C0 [& ?3 X. e5 X
   ::std::vector<int> a_;" F; n, I3 `; p) G
   int i_;
& h5 S) K( G- n   double d_;3 F" }( ~- Q. F. s2 \- n
   ::std::string s_;
. n% H- a! b$ R$ E/ g  D) m0 Z};
& \1 }5 R- v* `' G8 j$ a上面列出的三个特殊成员函数都不需要写。好处是明显的:当你增减 T 的成员变量时,你不必到
" P! i7 h: l& T. v5 y8 WT(T const&) 和 operator=() 中去相应地增减。
) a( p" z  {7 E* I; X6 N/ G
& l; R8 h; F1 r8 x( l+ r" W, z2 v3) 没有现成的算法可以对 CArray<> 进行操作,而标准 C++ 里的标准算法大多都可以直接在6 q) J, B! c+ y
::std::vector<> 上运行。例如:% n: i% E" L2 D. d9 R
  j# n  B7 l" [2 h3 Q4 l
static int const init_vals[] = { 3, 1, 4, 1, 6, 9 };
9 t3 Z9 T/ @; k4 Y9 k; q6 Vvector<int> a( init_vals, init_vals + 6 );# d9 C3 @$ N8 k( l  Y5 _# t! G
*find( a.begin(), a.end(), 6 ) = 5;    // 把6改成5
: z7 Z; v. W% b% t9 _% b1 C4 Asort( a.begin(), a.end() );    // 排序。3 R- a# j* z$ m. o
可以说,CArray<> 的主要设计错误是把一个本来应该是一个简单的“值”类型的东西设计成一个难用的“对象”类型了。所有的“值”的好特性都丧失了,但那些从CArray<>继承的派生类呢?
; Z2 y! H7 B- _9 V. f0 {, r- t$ o5 J! C' c7 W( q
CByteArray等的问题与 CArray<> 的问题一样,甚至更多(例如,CPtrArray,永远不要用)。& l$ U- W) L( e9 ^

8 R0 e6 q7 e' [) U: X同样,其他的 MFC container 模板,象 CMap<>, CList<> 等,都有类似问题,都应该用: n; a! x2 b, I( @, [# S3 a! ]* G
::std::map<>,::std::list<> 等设计更好的东西代替。
# \8 S0 E9 C: O; C
9 q2 }7 _. @' X- Q) t# {8 x6 y2. ::std::vector<> 在哪里?
5 o9 S$ N% Y3 S( l* c/ M; X, ]' W::std::vector<> 在头文件 <vector> 中定义:9 s& H% h" h9 ^

8 Q, g6 ~0 k+ ~4 Y! j6 l+ u(注意,标准的 C++ 头文件都没有 .h 后缀,有 .h 的文件是与 C 兼容的,或支持老的不标准的东西,象 <iostream.h>。)' d$ Q3 V6 e4 `" k, ]

7 j2 u0 r! f: e6 B# H( s) s; snamespace std
, j1 q8 l: `: E- ~( Y1 ~' y{( a' k7 |. S5 x) @# k' D
    template<typename T, typename A = allocator<T> >+ S- _( c0 A* C' K# o* m% h+ _
    struct vector4 _9 y( D  i4 ^& M
    {/ l4 z9 \1 [$ R) M
        // 具体内容稍后讨论
1 Q* y6 \6 ?% y( v* {& e7 W    };
! i1 D( E1 g- u; Y; g- k$ k0 w, C& X, Z; M% V: S. {- K

1 x5 E) S8 W4 \5 @3 o5 i, V    template<typename T, typename A>  h5 {5 g( @% S4 b3 `, I; O2 K: p
        bool operator == ( vector<T,A> const& a, vector<T,A> const&    b );/ z8 ?3 |9 X) v
    template<typename T, typename A>
/ C# F* z& b8 o+ L* I" y+ [        bool operator != ( vector<T,A> const& a, vector<T,A> const&    b );9 p2 `6 I3 \) M# q; u0 k
    template<typename T, typename A>; d5 ^' f$ A* \9 }3 E  ]7 g
        bool operator < ( vector<T,A> const& a, vector<T,A> const&    b );# o# f" j7 G3 Q$ ?, ~. u  @
    template<typename T, typename A>
$ S& u8 M8 [# I0 ~; N        bool operator >= ( vector<T,A> const& a, vector<T,A> const&    b );
8 A  o1 Z! u# U& r0 e0 @    template<typename T, typename A>; B- L/ l- A) t1 g
        bool operator > ( vector<T,A> const& a, vector<T,A> const&    b );7 W5 G" d3 |5 e4 S+ y+ Q8 D" m4 P
    template<typename T, typename A>
5 |" }: \' H& e4 n, `6 r  ?        bool operator >= ( vector<T,A> const& a, vector<T,A> const&    b );
( _) {& E4 i3 L% ~/ g  F/ N}
1 \: ]5 O+ }9 d* A9 X% _5 w# vvector<> 定义在 namespace std 中,使用时为了减少击键次数,通常使用一个类型定义缩短类型名称:
& o( X4 m8 ?/ \% _$ r5 K7 q8 _) P# d' N% {  C1 d% r
#include <vector>
  {: ]' D, b% [/ J3 @* Vtypedef ::std::vector<int> IntVector;
# @8 Y; c& @# Y$ cIntVector a;9 g) a6 t; E* T5 s
IntVector b( a );
% \. D5 _2 i5 s$ W1 eIntVector c;+ k; p, n7 }  X+ j9 Y: D
c = b;: G2 E7 V& f3 t- z5 O8 e' n
assert( a == c );
( l  ^0 B6 M# J请注意 <vector> 中定义了六个 vector<T,A> 的比较函数。这些函数只在真的用到时才会被实例化,才会要求 T 也提供 operator==() 和 operator<()。  G, X) p& Q" o! l6 L+ w7 K

; C) j! b- j: t. i1 S6 f; d另外,A = alloctor<T>:用于提供一个用户定义的存储管理类。由于这个参数很少用到,而且在 VC++6 的实现中有问题,不能用,因此以下的讨论忽略这一部分的内容。
9 ^6 L+ d, y/ a3 t
* U, ^  `$ Z' N# E2 J3. ::std::vector<> 中的类型定义
' ~( W) K+ z' Q, I4 t8 O& ?$ g: Jvector<> 中定义了一些类型,下面只列出常用的:  p6 M4 E1 L5 T$ ?$ A& C

/ p& ], [) n9 Vtypedef T value_type;
7 i9 Z& a1 q8 Z! Ttypedef T0 iterator;
7 J; G3 X5 ?: y5 `# T. U' f! V4 _typedef T1 const_iterator;
/ f2 l# a" v" b* r9 B# Ftypedef T2 reverse_iterator;* F7 j  b! @$ v/ j& }8 ^4 h
typedef T3 const_reverse_iterator;
8 F; H; Q7 a  [6 T
' f: u* q1 Z1 J9 g2 Rvalue_type 就是 vector<T> 的元素类型,也就是 T。当写通用的算法处理任意类型的 vector<> 或其他容器类型时是很有用的。8 I' ]& a6 O# d% F% y  Y7 y

& C' a0 O' T, u1 J  [( ^iterator/const_iterator 是两个 vector<> 的实现定义的未知类型,用于访问vector<> 中的元素,类似于 T*/T const* 指针,他们的区别是一个指向的元素可被修改,另一个只可以读:) I6 U( Z  ]: }! |/ p9 K
2 r# e; V" g5 i% o
typedef ::std::vector<int> IntVector;' r% \  A0 g- Z; j( O5 \5 D* @! `9 S
IntVector::iterator iter;
6 m! C- f! j8 h- x6 yIntVector::const_iterator c_iter;4 t& k! m! L6 S' S
// ...3 B5 l5 ]4 a  ?( q, K) B
++iter; iter++; // ok: increment, post-increment.
, j; X4 e- X: T8 k( `- R0 G7 ?--iter; iter--; // ok: decrement, post-decrement.
! C- B! v9 [8 d0 T( X++c_iter; c_iter++; // ok: increment, post-increment.
$ a/ A  p3 q& O: ?--c_iter; c_iter--; // ok: decrement, post-decrement.; S+ C  v: s3 Y% e
*iter = 123; // ok.
/ [5 m# ^6 G8 Sint k = *iter; // ok.
- {( p# H: d6 K  A; D. |. sk = *--c_iter; // ok.
# j8 F; X5 q' W& {" K2 K! |*c_iter = k; // error.9 t4 H3 X0 N: ~2 a+ Q: Q' S
c_iter = iter; // ok: iterator is convertible to const_iterator.* q( m  x% i. I
iter = c_iter; // error: can't convert const_iterator to iterator.
* u6 H: ^0 t; i0 E9 x) O) J1 P/ M6 c在使用上 iterator/const_iterator 和 T*/T const* 基本相同,事实上有些vector<> 的实现里就是用 T*/T const* 实现 iterator/const_iterator 的,但又不可以把 iterator/const_iterator 当作真正的 T*/T const*:+ G5 ?) X9 X: D! t; C

( K; J4 }4 Y4 u2 [7 ET* p = iter; // may fail to compile.
1 \, F. O  b, D/ RT const* q = c_iter; // may fail to compile.) c% w! P' v  f6 ?$ \
reverse_iterator/const_reverse_iterator 与 iterator/const_iterator 类似,但以相反的次序(从尾至头)访问 vector 中的元素。
; @2 i: ]* o1 Q; l3 X0 b
  I8 J+ \6 K% M, r+ F! a各种各样的 iterator 在 STL 中有特别重要的意义,但这里我们不做具体介绍。只要理解通过 iterator 可以访问 vector 中的元素,大概相当于一个指示位置的指针就行了。
& f! K$ j" G# P( \) k
3 M% N  f& D1 h0 f; J- q. R4. ::std::vector<> 的构造
9 z& L2 a4 g# c7 O* Gvector<> 提供了以下构造函数:(忽略 allocator 参数)
' ]: i( V2 ]/ Y8 J* C' W
9 N, s1 z) @# c" X$ h5 ]. bvector();
, I9 u1 H' I5 e+ K9 @: Nvector( size_t n, T const t=T() );# ?' L: [$ a. Q' g6 q8 O  Q
vector( vector const & );
( R) F5 ]' e0 d. e+ }7 zvector( const_iterator first, const_iterator last );' ^7 O* X% T2 U. ~% r& a* k) c
1) vector();
- q9 [8 [( w' a) N
( A) C" b+ @2 s$ u) ]$ v构造一个空的 vector,不包含任何元素。# y  i- v! J% `8 S$ _9 m9 g

9 L. v5 ]: x( o3 q( _" lIntVector v1; // 空的整数向量。8 a0 E2 H0 |/ V; z- I4 f. p4 h
2) vector( size_t n, T const t=T() );
* N+ f3 G- D0 K0 b/ c: z3 e5 T! E& n( A# i* ^3 G4 z# o
构造一个 n 个相同元素 t 组成的 vector。如果不给出 t,那么将用 T() 做缺省值:7 I- {$ v: x8 ^
, ?( |3 q% Q2 }  I2 V
IntVector v2( 100, 1234 ); // 100 个 1234./ F& _/ P. B) l3 D$ g
IntVector v3( 100 ); // 100 个 0。+ b  h- }6 w- Q  Q3 s, i& c2 b
3) vector( vector const& other );
6 W/ {7 K, d9 u: v2 L5 k: S1 n, J
( y. e2 K- D6 g/ P复制构造函数,复制 other 中的内容:
+ q) L! J/ n2 M) u! F4 H" a
) U1 M# b+ I) g' c, ?IntVector v4( v2 ); // 100 个 1234。% B! X- U, n8 i& n
4) vector( const_iterator first, const_iterator last );
6 [+ C2 e& f8 z& y0 @# a
7 z/ |; Y- }2 `, o; [事实上,这个构造函数应该为
! D+ _. L* x3 k( j& W, Q
& i( R' `6 M- F" c5 X! Y3 i) ^template<typename Iter>
% p- c0 [1 e. G7 c/ p/ H; \    vector( Iter first, Iter last );
4 f( l: S3 L1 |1 ]  K即拷贝任意的序列 [first,last) 到 vector 中。由于 VC++6sp0 编译程序的限制, Iter 被换为 const_iterator 了。不过,碰巧 const_iterator就是 T const*,所以可以如下使用:
& K/ ?# O. S# b: F0 ?2 T0 z* a
* J+ [) C6 y% b/ P& }4 ?. j" mint a[] = { 1, 2, 3, 4, 5 };
% B, ?/ [5 ?+ b0 \' Y2 TIntVector v5( a, a + 5 ); // {1,2,3,4,5}
; Q/ v! d  S* K: r& d6 L6 KIntVector v6( v5.begin() + 2, v5.end() ); // {3,4,5}: M" c! _7 }0 j5 H+ y, Q
5. 访问 vector<> 中的元素3 W- n* S  E) d' A0 k! Y5 t
以下成员函数/运算符用于访问 vector 中的一个元素:8 \7 C5 h2 V* Y! }+ A! s
) t" I$ Y9 g, F
T& at( size_t n );+ v/ ]3 P& @2 e$ p
T const& at( size_t n ) const;. M; ?" A; o# C/ f8 i
T& operator [] ( size_t n );$ N+ Q# F) f$ O. G* c  p
T const& operator [] ( size_t n ) const;
2 B$ G; \" k! I% |1 yT& front();6 @% W: N- Q/ }- v
T const& front() const;2 I" I5 i6 L- Z
T& back();9 A( G' Y3 p4 {) b4 x# |
T const& back() const;
9 Q9 q) A  @% [3 }请注意,由于 vector 是一个“值”语义的对象,所有的操作函数都必须严格保证 const 的正确性。所以,所有的元素访问方法都有 const 和非 const两个版本。  f9 Y& H) ]" Y* S6 \

5 K% y; V8 Q6 b$ q2 Sat(n) 和 operator [] (n) 都返回下标为 n 的元素的引用,他们的区别是,at() 进行下标越界检查,若发现越界,抛出 range_error 异常,operator[]不进行下标检查。
( P  ^5 D) c2 A; a& \. u  K5 [- }/ e
front() 返回下标为 0 的元素的引用,back() 返回最后一个元素的引用。4 I9 g+ }2 x9 U! Q3 a
5 @- p  q- h( @7 P& k% p1 b
int a[] = { 4, 1, 4, 1, 5, 8 };& k8 a! X$ E# E% k
IntVector v( a, a + 6 );
8 U5 u9 F& z5 j/ p// 使用 front(), back():3 [# N' H2 n$ ~& D+ K* Q
v.front() = 3;$ P$ V2 s: U0 D/ |# T3 U% w0 o
v.back() = 9;# h1 U) z; g" w/ ^* G2 M4 V
// 使用 operator [] ():# W' n: M8 o5 O/ t9 w' l
for( size_t i = 0; i < v.size(); ++i )8 P8 J: s0 s4 e. B, _; L7 n7 E1 X
::std::cout << v[i] << '\n';7 r5 i" @% ~8 n, j# ?* b6 v
6. ::std::vector<> 的存储管理) u$ P: h- w2 P. i6 Y1 B$ i
以下成员函数用于存储管理:
( N: S& a( U5 @
8 P1 Q/ g0 Y# s% x: y2 u6 _  v1 @void reserve( size_t n );
" v+ u  R2 `4 g) F. j% asize_t capacity() const;' n6 |/ \( J# U
void resize( size_t n, T t=T() );8 n5 u9 y7 T  ]) U7 i
void clear();
  |4 c: R  b3 f& i$ }6 K3 F+ Hsize_t size() const;
" n% R* U  R0 p; abool empty() const { return size() == 0; }  j/ l7 K$ m4 T. }- M
size_t max_size() const;+ T' E( K( \: A% \: i

4 }0 C3 E4 z5 j$ D  s1 [* d另外,push_back(), insert() 等也涉及到存储管理,后面另行介绍。* K" @4 W7 v1 X
, S' P+ M: s$ j
1) max_size()5 F3 @& z9 b  C8 h! A1 |

+ P8 y* Y, ?2 f返回 vector<T> 理论上可以装的最多 T 的个数。这只是一个理论上的数字, 大概是 4GB/sizeof(T),没有多大实用价值。在程序中不要用。
3 ?  J0 Y4 A: e# s4 d
, ?, a1 t- Y9 [0 `/ f* ]2) size()& W& i( U6 _0 J) x, C

7 ?9 Q4 [- H: A9 {返回 vector<T> 中实际装的 T 的个数。相当于 CArray<>::GetSize()。8 d  M8 z. T6 l+ v
1 T' w: C3 _9 W; g. }+ O( {
3) empty()
& R; b) p; w+ |4 R5 c$ W  h5 c. C! s3 s8 ?" h1 d& G
如果 vector<T> 中没有任何 T 对象,返回 true。也就是返回 size() == 0。
# y% p2 U4 H/ h5 b
' k/ L) Y$ W/ i4 ]* A# X4) clear();  f4 L3 B# I( f
( ?/ b7 E1 T+ N2 X
清除 vector<T> 中的所有 T 对象。执行后 empty() 返回 true。大致相当于 resize(0),但不要求 T 可被缺省构造。相当于 CArray<>::RemoveAll()。* n3 W/ o6 C2 S. x
4 S  i! \! }6 u) Z% p
5) resize( size_t n, T t = T() );) Q# l' l- j/ e5 K% b% d
: U5 A' g% Y8 U
将 vector 中的元素个数设置为 n,n 可以大于 size() 也可以小于 size。如果 n 小于 size(),那么 vector 中下标为 n..size()-1 的元素都将被解构。如果 n > size(),那么将在 vector 的后面新增加
2 g- V  [4 r' X' U- u2 P  `, _( qn - size() 个相同的元素 t。在增大 vector 时,可能发生存储再次分配。总之,调用resize( n, t ) 后,(size() == n) 成立。
+ g: s. S. T4 l0 H+ ^; n( f' j$ r! Y# }# ^3 Q( ]; @1 T
请注意,如果调用 resize( n ) 不带参数 t ,那么 T 必须可以缺省构造。& C$ H7 j, K% e; b$ c2 ]! @
# j/ f' L4 @- |6 t
6) reserve( size_t n );
' \: W' Q6 H" O  [& G
% ]6 X. L4 ?! ]- @2 W5 |事先分配至少可以保存 n 个 T 对象的空间。调用后 (capacity() >= n)成立。
3 f3 a6 Z* U9 t4 p3 G: X) G8 f# P: x
7) capacity();5 h0 P' R) ^! i' I! p
/ ]5 ~* ?3 U5 |0 J
返回已经分配的存储空间够容纳的 T 类型对象的个数。后续的增加元素操作(如 push_back(), insert())如果增加元素后 vector 中的总元素个数不超过 capacity(),那么 vector 的实现保证不重新分配存储空间。
$ o- \8 ?" o" t  i* c' }2 P) l0 R: v9 I1 F
vector 管理的动态存储空间是连续的。执行操作- R# y& w0 r+ h2 s
9 i) D5 q* u1 \9 }  W# I
IntVector v(7, 1); // seven ones.
9 ^: `+ G: H. R; rv.reserve( 12 );
, L' j# N" s0 N( |% @后,v 的状态可以用下图表示:
5 J+ a  V& ]; x9 r# U1 I/ X' M
6 m% L. w; @* D5 W0 u /--size()---\- z, |' A; C3 O" D) m
|1|1|1|1|1|1|1|-|-|-|-|-|2 q/ w* e7 V3 j  Y4 i
\--capacity()---------/
3 a0 F& }0 ^6 `- _6 O/ w, Q$ D0 w' W其中,1 是已经构造的 int 类型的对象,- 是可以构造一个 int 类型的对象,但还没有构造的原始空间。再执行
9 y  t+ w) l7 m: C5 U
7 q4 D' w$ P& d/ g' E9 e+ l$ a" b0 bv.push_back( 2 );' N9 s8 P. Z6 y: l( Q3 g
v.push_back( 3 );
. L. s6 P5 u+ a9 T" I+ y9 H+ U后,v 的状态可用下图表示:  h# q- w- e- g( C3 t, N; l' |2 {7 k4 ]

3 e8 \; q( x0 t: i /----size()-----\
9 |* x" S6 n- e7 w8 t! k|1|1|1|1|1|1|1|2|3|-|-|-|$ J( k6 h! |1 F! g, j0 G7 s
\----capacity()-------/
5 J. x3 _/ e# `1 z% o执行 resize( 11, 4 ); 后:
- w( x2 R+ n- i3 N4 R5 }& T
# e: c" q, Z8 S5 S, r- U /----size()---------\5 [8 B' n8 C$ E0 i0 w& r
|1|1|1|1|1|1|1|2|3|4|4|-|
6 T  H3 G- i2 p \----capacity()-------/
3 @# k/ w; [. G# |; |0 Jcapacity() >= size() 总是成立的。对于下标为 [size()..capacity()-1]的未构造对象的存储空间,是不可以访问的:7 F& q" h8 I3 \9 \$ p

4 }$ J! R; S( a" H9 Yv[11] = 5; // undefined behavior - anything can happen.
9 g+ c. Y5 R6 L2 L1 Y, F% ?7. 添加元素到 vector 中
5 a( ~6 E' q2 t, L! c) p  m6 m下列操作添加元素到 vector 中,并可能引起存储分配:* F* W8 J4 |" i) x1 X

7 ?! @1 `# \  P3 ^# A7 m2 Q- L& Fvoid push_back( T const& t );: X( z% t) \) d, ?: N4 l. ]
void insert( iterator pos, T const& t=T() );
" N8 z+ U2 T2 v+ _7 }& Ovoid insert( iterator pos, size_t n, T const& t );
2 D5 X, t/ L( E+ x+ s+ j! N" |( otemplate<typename Iter>: K* C& O4 k8 s2 [
    void insert( iterator pos, Iter first, Iter last );3 v9 Z0 i+ p! m0 {
push_back() 是把一个元素添加到 vector 的末尾。insert() 是把一个 t,或 n 个 t,或从 first 开始到 last 结束的一个序列插入到 pos 指示的位置之前。% k+ ?5 j* d! r- y: r: j/ R4 _
, v6 l5 \& ?. c- X
当插入元素后 size() 将会大于 capacity() 时,将引起自动存储分配。vector 将会分配一个比需要的存储区大若干倍(通常是1.5到2)的新的存储区,把老的元素拷贝过去,同时完成添加或插入,然后释放老的存储区。
- {  t! b- R8 }) X+ i  l; B4 Z3 S9 W7 \4 M" K
这就是说,vector 自动存储分配的空间大小是指数式增长的,这可以保证多次添加元素到 vector 中时,平均用时是接近于常数的。
7 y/ C. m$ H7 v. u  c+ Z- {) J/ E% f- i5 K( w
IntVector v;
: w, ?% e. j- r" ]! U' l   $ [6 N- s% u/ F* M
// add 0, 1, ..., 99 to v:
  h) y9 w! j3 yfor( int i = 0; i < 100; ++i )
* P+ f4 Z/ T7 ?2 N% Ov.push_back( i );0 k9 @' `* y2 a7 R7 _1 @- H9 U' \8 _
   
2 L! f# D* _9 U3 T9 c// append 9, 8, 7,..., 0 to the end:
- ?! N: |) J" V2 I: a. L) Lint a[] = { 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 };
8 c2 P+ D8 s) }: b! _. Fv.insert( v.end(), a, a + 10 );2 V5 Q7 o5 Y5 K! w) u7 w- P& S; ^
8. 删除元素
+ e( ^2 w8 p  I9 K下列成员函数完成元素删除:+ i" ^: F$ n7 e4 a
% q# \0 r2 T  n' L
void erase( iterator );- S/ ^1 {( |% m) C0 \
void erase( iterator first, iterator last );
- p( S/ o2 r3 K1 I3 c/ H- H( uvoid pop_back();4 e! L- {' v; F7 |) E* |' [
void clear();
9 ^+ X6 a6 Q) F; n这些函数分别删除一个,一串,最后一个,或全部元素。
2 v+ D1 o; H5 ^8 N/ L9 m( |, |& v5 W# ?
IntVector v;
1 w$ `) x) \. k0 h0 O0 Bfor( int i = 0; i < 100; ++i )9 j: X: j4 [+ h7 _
    v.push_back( i );1 ]) m5 t, r5 u! K( z/ R
   
" q3 ?4 c  y& |: l3 m' Z// 删除 50, 51, ..., 89:
2 Z. \2 T' s3 e% b" I! B# lv.erase( v.begin() + 50, v.end() - 10 );( c  D) L1 b3 ?% p+ k
   $ D& g6 J' K. m; [: g) v/ @% d6 z3 a! p0 l
// 删除 49, 48:! K! [6 {6 @5 t
v.pop_back();8 v5 A1 N/ ~% _9 u% {
v.pop_back();, E& o  f% }9 X. Y( }7 b; d
   
& [  ~- O: k6 O& K7 Y  A5 J1 w// 全部删除:
4 ], o$ g, l* ^3 Wv.clear();
. ~# R4 S8 \$ L" {: R$ U注意,删除操作不会引起存储分配,因此 capacity() 不变。0 }' j0 |5 I7 w* M9 ]7 H
: E9 W" ~4 `9 Z" R- z6 K1 ?
9. 作为序列访问 vector 中的元素
" l; ?" k1 _- b) r序列(sequence)在 STL 中是一个非常重要的概念,所有的容器类型和算法都涉及到,而且所有的算法都是建立在“序列”这个概念之上的。+ p$ z2 G1 e- s- h1 U8 y* x- F
/ J* @2 @% s. C) A. Q* e, h+ R
“序列”是一个线性结构,由一个指示其起始和一个指示结束的叠代子(iterator)来决定。如果 first 和 last 是某种类型的叠代子,那么经常用[first, last) 来表示一个序列。注意,first 指向的元素是这个序列的一个元素,而 last 指示的是这个序列最后一个元素之后的位置,可能根本没有元素可以访问。这种半闭半开的区间表示是整个 C++ 标准中的约定,而且确实可以简化程序。( J+ G! ?9 e4 s% N7 B
4 _, Q" W) |& e& 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 的要求。
% o$ ?8 t; \) ^% S; k
9 D& O" R( Y9 ^0 \0 h; wvector<> 中定义了以下函数用于获取被控制(管理的)序列(动态数组)的各种叠代子:+ E4 q5 B/ r0 |( u# u" h
$ J8 _8 t( }+ u5 S3 K. h- u
iterator begin();( j# z' i* r. L+ _2 n6 x
iterator end();( L" i6 ~, T; x  S: z0 H1 n8 w7 G
const_iterator begin() const;
9 j( \8 B( q' J5 c# A5 oconst_iterator end() const;- l4 P3 Z6 r$ J- ?
reverse_iterator rbegin();
' U0 {5 `, |! N! qreverse_iterator rend();6 W6 X9 P8 ~4 o
const_reverse_iterator rbegin() const;
; v) C& K7 F# X, ]% h# aconst_reverse_iterator rend() const;+ M) ?$ n* @. e$ u8 Z: }1 e$ f
这里我们不讨论叠代子的一般概念,只举几个 random access iterator 的例子:
& d. X" y8 d% m; N* I
6 J+ H  k' `% |( Pint a[] = { 1, 2, 3, 4, 5, 6 };
+ R9 S3 x. z2 n' R, @0 F' ^5 y* _[a, a + 6) 是一个随机访问序列,指示了 a[] 中的所有元素。这里叠代子的类型为 int*。2 x2 ]  }" _9 g" ^$ e0 g

% u0 D; `/ i# \' L; N, }* \1 |) r[a + 2, a + 4) 也是一个序列,指示了 a[] 中的 3, 4 两个元素。叠代子的类型仍然是 int*。( w6 e3 d8 q9 L( h" Q
1 T8 W$ K3 _9 ?/ X% E
IntVector v( 100, 1 ); // 100 个 1。2 X" [6 _7 z# A1 r: z4 y
[v.begin(), v.end()) 是一个随机访问序列,指示了 v 中的所有元素,叠代子的类型是 IntVector::iterator。7 l: A  a1 H! w& H

( |4 u# s$ @- i: u5 p5 ^- m[v.begin() + 10, v.end() - 20 ) 也是一个随机访问序列,指的是 v 中除了头 10 个和尾 20 个元素外的其它元素。" m. M+ L/ T7 @' [& Q  c
, z5 c$ L) P- Z! m" g6 F/ a) `9 x
[v.rbegin(), v.rend() ) 是一个随机访问序列,指的是 v 中的所有元素,但与 [v.begin(), v.end() ) 不同,这个序列是从尾到头遍历所有元素。0 ]3 z8 o) A( U6 c  g/ v+ x
* R' C( d% ]4 W. Z: h
[v.rbegin() + 20, v.rend() - 10) 与 [v.begin() + 10, v.end() - 20 )指示的元素相同,但遍历顺序相反。3 ?- c" k% m+ G/ Q# E$ f& Q- [3 h

4 L2 ^7 S+ A, x0 q) p下图是有十个元素的 vector 的 begin()/end()/rbegin()/end() 的示意:$ I  ?2 I; v) }2 k8 u6 L" b
9 j" \% h+ V2 T, g! ~
begin() ----------> end()# q+ R5 J- j, j% l6 E
  |                   |) a0 R) Y' |( ]$ [/ E
  v                   v0 q/ B: K! i; t9 k6 s
|0|1|2|3|4|5|6|7|8|9|
$ e! Q: H. I! K/ C1 F# g1 T^                   ^* K( L7 W) A* H3 _. ]( H
|                   |
' ]$ l2 W  Z. S0 [. v7 Wrend() <---------- rbegin()
8 W# Y! W/ C- B7 ~4 b* A( {   + P+ n" ~/ v/ e! i" G* a$ k
IntVector v;
& K) p, ]0 |  n% {( @for( int i = 0; i < 10; ++i )
+ i4 J0 |! n5 bv.push_back( i );
( O& W0 ^, {/ A! m" c& ~   , a1 e% q3 a8 f4 S' |* ?# \
// print 0, 1, 2, ..., 9:; ?) @* Q( I( l7 t! @, _# R- k
for( IntVector::iterator i = v.begin(); i != v.end(); ++i ), Z. ]9 X$ A0 L/ O3 F- ?3 G9 ]9 i
::std::cout << *i << '\n';
6 v$ e# f) v3 P, }* l) e   
  t# z0 l2 }& h3 M# X// print 9, 8, ..., 0:
, k. P1 Q" ^8 t, p8 U- pfor( IntVector::reverse_iterator i = v.rbegin(); i != v.rend(); ++i )9 f4 u* t2 w4 }; G: x' [
::std::cout << *i << '\n';: ^* Y8 U4 E* U
除了使用 begin()/end()/rbegin()/rend() 来遍历 vector 中的元素外,由于 vector 管理的空间是连续的,因此可以直接取地址进行处理:+ b" P0 ~% _$ u( C7 _$ c8 F

* z) g5 t" G# d( g0 S( Y, f::std::vector<HANDLE> handles;+ @* Q) K. O, _  r8 H; W7 B
handles.push_back( handle1 );
- `1 P2 c  U: q. a) shandles.push_back( handle2 );
8 \2 E+ i# s4 r7 Q3 J  y9 E; x! |* a" T9 T) d
: ^* O0 ^2 u1 H* ?+ f$ V
::WaitForMultipleObjects(handles.size(), &handles[0],TRUE, INFINITE);
: }8 W* d' g$ m/ y  [这在与 C 库函数接口时尤其有用。4 K$ i& X/ b8 z. ~
' z4 A4 m$ {" _2 Y- H) F" V
10. 赋值和交换
. k2 c7 J8 ?. `  E: y5 @& d9 Kvector<> 是可以赋值的,这也是一般的“值”类型必须提供的操作:
' E0 _/ ]9 V7 p" O
4 t, G! G5 y# fIntVector v( 100, 123 );6 c! l' {" W' X% a, u; o& N) W% t+ |
IntVector v1;
$ X" l0 h2 F1 d) n9 q7 xv1 = v;
0 S( O- }/ {6 p5 }' Ivector 另外还提供了) w& O! M3 c- n

* _# w6 r6 f0 }0 r) p, `template<typename Iter>
3 u2 F9 h5 d8 Vvoid assign( Iter first, Iter last );+ p$ V* B) ]3 l6 d) Z3 u
void assign( size_t n, T const& t = T() );4 _* Z  u- c  _$ C: t/ a
用于赋值:" w7 `5 q* ^; Q, c5 e3 Y" l; `# q

3 ~- W$ Z( M3 {: i: I, \* M; Aint a[] = { 1, 3, 5, 7 };! o' J1 g  O" c9 C# F0 [
v.assign( a, a + 4 ); // v 将包含 1, 3, 5, 7.
7 L: J: Y# o  E4 b3 w, F; pv.assign( 100 ); // 100 个 0。
- f' m4 u9 p; V, K5 c, [还有一个很重要的操作:: w2 S6 t9 d% R

0 n4 G( v, i- K; S6 f. a3 b/ c; Jvoid swap( vector& v ) throw();2 O: v6 f( G0 c1 @7 k5 f
用于交换两个同类型的 vector 的值。它的特点是快速(只需要交换内部的三个指针),不产生异常。这在写一些保证异常安全的程序时非常有用。+ _* I( a9 u2 t5 I
6 a. P7 v; A7 f
事实上,swap() 基本上已经被当作类似于 operator=() 的一个“值”类型应该提供的基本操作,::std::swap() 也应该为用户定义的类型进行特例化,调用相应的类的成员 swap() 函数:
0 z3 n* z8 w! f( f$ C0 t/ v
$ Z( J5 Z) p2 g1 ?& Y0 T0 P) gstruct MyVal( f6 l3 v/ }5 w; V; T+ d6 i! f
{
; t  k. O/ E. p+ L: {6 E3 U: m  // blah blah.. g9 T3 j0 j. z) g+ p
  void swap( MyVal& ) throw();
( H3 R: r3 V. d  y' v};
" D" ]+ W& `# l. h- @   ) z, k' E$ [# J) e! p8 o  w
namespace std {1 y$ w7 W9 Y5 H' z$ n& x8 U" Y
  template<>7 O/ k  A" ~1 g$ K* t
    void swap( MyVal& a, MyVal& b )
0 |, R6 i- E( l' b/ F7 {( H0 c    { a.swap( b ); }* \# i" p4 [! m' [
}
5 i$ I. [* h% q! a$ _关于 swap(),值得专文讨论。这里我们只指出,vector<T>::swap() 是快速的,不抛出异常的,很有价值。
$ e: j6 B4 ?* b% v" B: `
9 N* B+ w: v6 G- K11. 使用 vector 时的存储管理策略2 @" O' z* K( O, m" q. b
从前面的介绍中可以看到,vector 的自动存储分配是指数式的增加存储空间,而且永不缩小已经分配的空间。这在大多数情况下是合适的。 如果应用程序事先知道要用到的元素个数,可以先调用 reserve() 来保留(分配)空间,这样可以避免以后增加元素时不必要的重新分配和元素拷贝:% S8 m: y! I* e! L% X

% A8 E0 F! Y9 ^IntVector v;% f+ _, h: W, ?  v: m2 n
v.reserve( 100 );7 X8 H& X( j: S. C! n
for( int i = 0; i < 100; ++i ): B' f* K) T- b3 Y! L+ r& o
    v.push_back( i );
& y, s/ y4 n5 u. b: K: }2 T8 {. P- R' v请注意,reserve() 和 resize() 是本质上完全不同的。reserve(n) 保留的是未使用而能够使用的原始空间,而 resize(n) 是真的创建了 n 个对象:5 I1 L( d3 B) a# w2 _- w9 X; ~, P

8 g* P3 ?5 s. WIntVector v;* L2 V# t% r+ Z3 T5 m
v.resize( 100 ); // v 已经包含 100 个 0.
3 W" h5 q; y' z% F, @9 K$ Ffor( int i = 0; i < 100; ++i )$ c9 H7 I/ R! T
    v[i] = i; // 可以赋值
; c' ?& E  z- c( Q有时候,一个 vector 可能增长到较多个元素,然后又减少到较少的元素个数,这时,可能希望缩小 vector 分配的空间以节约内存。CArray<> 中提供了 FreeExtra(),但 vector<> 并没有提供相应的函数。这时必须进行复制:# V6 k, |: k) E! w) w  ^; J
5 D1 h0 r5 z3 K# H" a. x' [7 e
IntVector(v).swap( v );
( k% f8 }3 ]- _2 B4 b+ X6 v有一种看法认为拷贝构造函数同时也复制了capacity(),而标准中并没有很明确地指出这一点,因此更安全的方法是
$ H9 S4 O5 j+ _4 R6 w# [+ l& E5 t6 r  k* |
IntVector(v.begin(),v.end()).swap(v);/ R& n4 k+ A! O! ]) S
如果一个 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-5-2 10:32 , Processed in 0.020521 second(s), 14 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

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