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

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

[复制链接]
发表于 2008-2-26 13:29:18 | 显示全部楼层 |阅读模式
作者:wangtianxing- `5 r, W. y$ E9 V0 \5 C; _5 @

8 O5 |# |4 D% d/ r0 M* p7 M6 c原文出处:http://www.cpphelp.net/issue/vector.html
- ~# N$ `, @0 F; N
! p$ k: \6 \4 r( m3 q. d, j. e9 q7 A7 f: Q/ S

( L$ h, r# G& z+ j摘要: 本文介绍了C++标准库中的容器类vector,分析了它的优点,并且建议在应用程序中使用它作为动态数组的优先选择,而不是MFC的CArray<>等其他类模板。最后介绍了vector的接口和使用时的注意事项。
. r- g7 g# o" O4 V) o( u0 t9 F) w  s! z8 B! Q3 m+ d1 C& W$ m% Z$ L
在一些使用 MFC 的程序中,经常看到许多程序使用 CArray<>,由于 CArray<>的设计问题,造成使用它的代码的复杂化,增加了维护难度。因此建议使用 ::std::vector<> 代替 CArray<>。6 a4 f; c% [& M' p
2 n/ V7 B( d- x9 S
另外,也看到一些程序在用 malloc/realloc/free/new[]/delete[] 等手工管理内存。在应用程序中,手工管理内存是容易导致错误的,应该用 ::std::vector<> 之类的对象来管理动态数组。
- ^7 D% N4 H9 `7 A" a& j# @2 |* ]2 a
由于 MSDN 中关于 ::std::vector 的内容较少,我们在这里做一些介绍,供参考。
! t* V: J: {) ~4 h! d, ^
8 O/ c* ?  L% w不熟悉 CArray<>/WIN32 也没关系,这里提到它们的地方并不太多。6 \) Z1 o2 p2 {# c2 }& @! p
  a. Q/ _/ N! N9 ]
1. CArray<> VS ::std::vector<> ?" C- h( ~- ?: g, s, B3 x$ C
CArray<> 和 ::std::vector<> 一样,都是模板类,用于管理任意类型的对象的动态数组。都在解构时释放所管理的动态内存。因此都可以用于代替手工动态数组管理。
# Z) y1 d" x- z
5 ]& R3 Y8 E) e' f* Z/ r  ]# ?6 q7 a但是,CArray<> 是在 C++ 标准化之前很多年(VC++2.0时代)设计的,当时对 C++程序设计,面向对象程序设计,模板程序设计等技术认识严重不足,尤其是当时对面向对象技术的错误信仰与宣传,造成 CArray<> 的设计有重大错误。( S, B9 v: Y9 I0 W' T. V0 Z* P

  U& V! m! [$ Y: X% ?在 C++ 语言标准化以后(1998),以及 VC++ 6.0 出世以后,提供了标准的::std::vector<> 模板,基本上在任何方面都要优于 CArray<>。Microsoft 由于要支持老的程序,因此一直保留了 CArray<>,但显然并没有打算按照新的思想去发展它(至少应该提供operator=(CArray const&)吧)。  V8 r# V& |" z

0 I+ I5 Q+ y4 `8 ?" [概括起来,CArray<> 与 ::std::vector<> 有以下不同:7 u- D# c( I# N. E5 f4 G5 g! A

1 {0 i/ H4 d7 T% m, c: B# L1) 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<> 没有继承任何东西,只是实现了管理一个动态数组该做的事。' X- H) J$ Z7 B/ ?$ H/ P

7 n3 A8 y8 W3 ~5 n' j2) CArray<> 不是一个恰当的值类型,例如下列操作都是不合法的:
, n. C4 K: j/ j  {) `7 w) L# k) `7 C2 I4 k) A7 A) w2 {* {2 u
CArray<int,int> a;
" ?" A2 C; G7 F, G. x$ gCArray<int,int> b(a);  // error, must use Copy()." L" h6 ^% ^4 z- |  F
b = a;        // error, must use Copy().
( d; @, x( _/ E7 v7 o8 m# Cb == a;       // error, you must write your own.& s+ ?9 s. i& Z
b < a;        // error, you must write your own.
  X, l$ Q' [- B; r, s5 u! T与 CArray<> 相反,::std::vector<> 是一个认真设计的值类型,天生是可以拷贝构造和可赋值的。如果 T 是可比较的,那么 ::std::vector<T> 将自动地是可以比较的。  m3 G8 `. t. M9 o1 W

1 ~$ p' Z" v% {此外,由于涉及到四个特殊成员函数;
( L9 b- G, S, A3 {
$ k9 Q" i0 R/ \4 M6 ]T(); // 缺省构造函数(default constructor)5 r/ f9 w* @2 G7 M, z; J
~T(); // 解构函数(destructor)
; D& v2 G- h* ~1 QT( T const& ); // 拷贝构造函数8 ?& k( y  K' U( V- j' o# A
T& operator=( T const& ); // 拷贝赋值函数. {4 N+ P& }3 {- y3 L( E
的自动生成,如果使用 CArray() 作为 T 的成员变量,那么上述的四个特殊函数中的后两个将无法自动生成,需要手工写:9 p$ H2 f' a/ [/ [
  ^! |5 m; Y/ W1 w. t
struct T
6 m; v7 p/ b: {" A. W! N{1 \9 v+ b8 e; D, z# j+ _
   T() {}" F+ d/ i& |& g
   T( T const& t )
; h  B) |4 ~4 Z0 M   {
- d- Z* u/ n; L0 \" j! O       a_.Copy( t.a_ );
& a+ t+ h# ~& d) `* k5 N& Q# H       i_ = t.i_;' u& L8 f3 _: V! |, F7 {
       d_ = t.d_;' N# p8 Q, `# y* q& Z# ^0 n$ q
       s_ = t.s_;& X: {6 q  x8 L* Z7 v4 Q& h1 @
   }. }5 V% U0 l; Z4 M1 e& p! f9 ]
   T& operator = ( T const& t )8 i+ A8 ^$ T1 ^) m" }
   {
9 s% _+ ^1 f, @" B       if( this != &t )- z- ]) t! c$ D
       {8 Q; S1 M& ?; |# |: z5 J6 X
           a_.Copy( t.a_ );
( q  F+ O8 |# P; \; l% L           i_ = t.i_;
. j; W$ J% A, c           d_ = t.d_;
5 w/ ]# ?! ~9 N( K9 F2 {           s_ = t.s_;
5 ?9 I3 ?6 o9 w% X' [3 g$ f. f       }( K! f% B1 i% f# |5 \
       return *this;) @6 N. e" d8 U9 |, a
   }
2 k8 y# c7 R' `+ E& t, iprivate:; z2 l3 ?- G" Z$ J" g( N/ I
   CArray<int,int> a_;
0 v2 s9 Q8 K# `$ Y2 R/ S/ h4 R   int i_;
2 E0 {, S9 e* M% D  j! \   double d_;
7 B0 \' V1 r, ~   ::std::string s_;! f* ]! p. l& ?1 Z! [, O/ ]
};3 F4 J& |6 F- J2 e% r
如果使用 ::std::vector<>:
9 i9 b# `; h7 f# K6 ]
( v& [6 ]" x2 R( fstruct T' n, Q- n- u3 J6 w
{
- ?6 P/ K* a( Q9 O+ u6 `3 Q# D7 aprivate:' _; _8 r- G6 N$ i; w; P1 t
   ::std::vector<int> a_;, K! x5 \. k1 C3 C8 k
   int i_;
2 v) `3 Z; S6 H. Q$ {% E: Z   double d_;
( o. |' g1 Q2 _' o0 B   ::std::string s_;4 {" m3 F& {; c+ g3 ?2 T
};
! G) p6 U; p2 B& D: G# Y1 j( `上面列出的三个特殊成员函数都不需要写。好处是明显的:当你增减 T 的成员变量时,你不必到
9 w0 V! [0 H+ Q( T- U$ RT(T const&) 和 operator=() 中去相应地增减。& V3 R6 u- M) x, y4 v" q

/ U$ b0 Y3 Z& b% {5 L. |; X3) 没有现成的算法可以对 CArray<> 进行操作,而标准 C++ 里的标准算法大多都可以直接在
$ {# a% \5 B. U4 e::std::vector<> 上运行。例如:" z* U; ~! l( y- I# d( X5 t6 x
4 O$ K0 v* B% G2 O; B- b% h
static int const init_vals[] = { 3, 1, 4, 1, 6, 9 };
8 `. h- C8 v; S2 ~  Avector<int> a( init_vals, init_vals + 6 );0 _  F& m4 ~: [# l+ q! U0 r
*find( a.begin(), a.end(), 6 ) = 5;    // 把6改成5
# _8 `# R; G. ^4 esort( a.begin(), a.end() );    // 排序。6 L9 v9 ?& h/ M7 G# D7 X
可以说,CArray<> 的主要设计错误是把一个本来应该是一个简单的“值”类型的东西设计成一个难用的“对象”类型了。所有的“值”的好特性都丧失了,但那些从CArray<>继承的派生类呢?' t. M8 L$ J3 Q1 V( l. S3 Q

. s8 J2 z4 ?4 C- |CByteArray等的问题与 CArray<> 的问题一样,甚至更多(例如,CPtrArray,永远不要用)。0 j. l# p. q! q8 q' ]) b) d, s5 B
. X& u  \$ v8 K2 Y
同样,其他的 MFC container 模板,象 CMap<>, CList<> 等,都有类似问题,都应该用
( T3 Q+ I0 U; O::std::map<>,::std::list<> 等设计更好的东西代替。
0 ]- T3 {# q) }' w- t) A4 J# J0 `$ h; [# V! ~) a2 \5 y
2. ::std::vector<> 在哪里?6 m. C$ w. C8 Q
::std::vector<> 在头文件 <vector> 中定义:
5 g* ?% Z" r8 `+ m: ^; T$ v8 h9 `/ s. V
(注意,标准的 C++ 头文件都没有 .h 后缀,有 .h 的文件是与 C 兼容的,或支持老的不标准的东西,象 <iostream.h>。)6 [4 Q- J* H2 _; V$ c

4 B* V4 R2 B$ D8 Q: g5 ?% O5 hnamespace std ! C: j8 o0 E* G& ~$ U5 m3 A
{
- Z8 d- x& l9 v$ H5 X5 l  Z" j7 p8 m    template<typename T, typename A = allocator<T> >+ D6 B8 p7 L9 V" Q! `5 g
    struct vector4 {6 n6 `% i/ w! t- i
    {
" R4 n7 c1 o* j$ D4 f6 |+ {        // 具体内容稍后讨论
4 S" S  H* i# V% `! l' A    };
$ k/ u/ o( L( {) k1 s3 }
$ I& ^$ ?* s  c9 l& D/ F
# t1 L% |/ P+ @    template<typename T, typename A>
" L1 w5 g) C- k0 I! I        bool operator == ( vector<T,A> const& a, vector<T,A> const&    b );
* J5 A; N4 J- ?" N: {    template<typename T, typename A>- V5 b& J0 I& s4 K( w" R0 F
        bool operator != ( vector<T,A> const& a, vector<T,A> const&    b );
9 g! g( W% X/ l; i    template<typename T, typename A>
3 k  g6 _1 @3 v0 \2 b- Q        bool operator < ( vector<T,A> const& a, vector<T,A> const&    b );$ s! o4 Q, F& g& w. A  c( n
    template<typename T, typename A>
$ T( k9 c- N: m7 E0 k        bool operator >= ( vector<T,A> const& a, vector<T,A> const&    b );
5 Y, [+ E, C0 D% b9 _+ ^' A    template<typename T, typename A>: H3 b' g% G* Y9 `& P5 {4 k
        bool operator > ( vector<T,A> const& a, vector<T,A> const&    b );
4 v8 S$ U" v4 P; v! g1 U+ v    template<typename T, typename A>
4 @" r8 v4 n( I- q5 H        bool operator >= ( vector<T,A> const& a, vector<T,A> const&    b );
+ g1 }( t) |( R, T1 H}
& D1 [( c4 q! f5 b5 ?7 A5 Mvector<> 定义在 namespace std 中,使用时为了减少击键次数,通常使用一个类型定义缩短类型名称:
5 [9 W- V* ~) ~
% H/ }3 e6 e% [: |, \; X2 I. z#include <vector>
' U" `! n3 y: }( s& x( J9 qtypedef ::std::vector<int> IntVector;
1 |+ R  K6 W) [9 uIntVector a;
3 E2 b  F$ k7 ]$ V- ]8 F8 FIntVector b( a );/ f% L& X; b4 d
IntVector c;/ R0 d$ z" a  @( O
c = b;
4 C8 P# o1 {0 u+ F( }. C, F2 }assert( a == c );
2 v3 Z5 U% r) t6 ~6 J  N请注意 <vector> 中定义了六个 vector<T,A> 的比较函数。这些函数只在真的用到时才会被实例化,才会要求 T 也提供 operator==() 和 operator<()。1 x8 t4 A9 M% s
8 r. _9 Z. Z( Z
另外,A = alloctor<T>:用于提供一个用户定义的存储管理类。由于这个参数很少用到,而且在 VC++6 的实现中有问题,不能用,因此以下的讨论忽略这一部分的内容。% p% H. R3 z+ `) W

& C2 u: ]8 ^2 x1 w1 X3. ::std::vector<> 中的类型定义
% O5 `1 `$ m6 r/ ~& w  y) Vvector<> 中定义了一些类型,下面只列出常用的:3 W$ z- g' U6 E) W
6 q! w0 n& h$ u, J
typedef T value_type;
, l/ Y9 ]& P- `, F. K7 S; ?typedef T0 iterator;
& w' k; x  q& q0 u' ptypedef T1 const_iterator;
6 Y3 B: x3 m  s3 Itypedef T2 reverse_iterator;' {0 G% J. N5 p6 n) d, b7 q
typedef T3 const_reverse_iterator;0 Q- g  v9 z& _; g
7 N' ?. W5 P) Y  n
value_type 就是 vector<T> 的元素类型,也就是 T。当写通用的算法处理任意类型的 vector<> 或其他容器类型时是很有用的。
' @4 J8 H2 G3 X  V$ L4 _
2 u/ h8 I3 ~0 Riterator/const_iterator 是两个 vector<> 的实现定义的未知类型,用于访问vector<> 中的元素,类似于 T*/T const* 指针,他们的区别是一个指向的元素可被修改,另一个只可以读:
0 {0 O* m! [( @. d
4 C7 R1 \$ h, T7 a5 otypedef ::std::vector<int> IntVector;
( h2 Q8 O  _- n2 o% e, w9 |IntVector::iterator iter;
; P' t- H0 T; TIntVector::const_iterator c_iter;* v. n7 H7 A1 x  o: u
// ...- I, X7 b2 ^5 d! ?+ [( P6 t( y
++iter; iter++; // ok: increment, post-increment.
4 y4 E& B  n& I- D* o--iter; iter--; // ok: decrement, post-decrement.' _& Z7 {, x! W/ X
++c_iter; c_iter++; // ok: increment, post-increment.
' \) l2 d, R/ w--c_iter; c_iter--; // ok: decrement, post-decrement.( p. f" F3 h- o( V
*iter = 123; // ok./ S; L7 ?# @' y2 M9 X. m: _+ _4 ?
int k = *iter; // ok.
/ h1 g  s3 K# z7 L" Gk = *--c_iter; // ok.( C; V' S0 @% d  Z+ _+ }% D7 _
*c_iter = k; // error.7 V* w  k! j0 ]# o  h
c_iter = iter; // ok: iterator is convertible to const_iterator.# ^( i8 [1 x2 n/ A, j# i
iter = c_iter; // error: can't convert const_iterator to iterator.) I( H7 z) Q! O. b
在使用上 iterator/const_iterator 和 T*/T const* 基本相同,事实上有些vector<> 的实现里就是用 T*/T const* 实现 iterator/const_iterator 的,但又不可以把 iterator/const_iterator 当作真正的 T*/T const*:
5 B7 o4 q  i$ m$ [8 O* s4 L0 {$ S. w$ ^3 l, u+ v
T* p = iter; // may fail to compile.: B' c: J2 r* u5 d0 d0 k
T const* q = c_iter; // may fail to compile.! ^+ Q9 S7 t4 r, P5 ^
reverse_iterator/const_reverse_iterator 与 iterator/const_iterator 类似,但以相反的次序(从尾至头)访问 vector 中的元素。) c* O7 o3 }: _# z2 H
5 g8 ]" z" o  B# B# J0 R9 N8 v/ }
各种各样的 iterator 在 STL 中有特别重要的意义,但这里我们不做具体介绍。只要理解通过 iterator 可以访问 vector 中的元素,大概相当于一个指示位置的指针就行了。6 Z# K2 T% C- C1 H9 _
* n' t, l! {- m
4. ::std::vector<> 的构造
" O! x% j! y6 c5 F/ ~9 g! ~vector<> 提供了以下构造函数:(忽略 allocator 参数)% M7 j# C2 o9 [. m, K% ^0 l
7 ^; b- U5 D+ K; }8 L/ G: [
vector();" }! x( K1 K7 }# A
vector( size_t n, T const t=T() );9 Y+ b: i- j" Q
vector( vector const & );7 Y0 U6 V9 k( K. o
vector( const_iterator first, const_iterator last );; l9 F/ G: c. e8 O0 A- ?. H
1) vector();
1 Y1 I& k+ t2 o! G# E# \$ f% X$ P/ ^
构造一个空的 vector,不包含任何元素。
6 V2 g  i3 H' G/ N/ V
# s$ D  H5 d, ]4 IIntVector v1; // 空的整数向量。
0 \3 D7 E9 z( o( _1 P, b2) vector( size_t n, T const t=T() );3 w0 v9 v- g* I' z$ T8 ?4 P

2 b2 _& Q  S6 q* b0 j构造一个 n 个相同元素 t 组成的 vector。如果不给出 t,那么将用 T() 做缺省值:* {7 B+ z+ n5 e" j+ X! l5 I

9 B6 u& O8 x- ^IntVector v2( 100, 1234 ); // 100 个 1234.5 d3 k) J+ H: h$ ~5 k4 A; \
IntVector v3( 100 ); // 100 个 0。8 r1 g, h6 T% q; v# t! M) `
3) vector( vector const& other );; g, Q& u& n# q/ S) `1 K% ^9 |

- J: j, q4 r7 K6 t复制构造函数,复制 other 中的内容:
1 Q; H4 M( K; J+ p8 D2 I. b7 A- g: D
IntVector v4( v2 ); // 100 个 1234。
" n9 v4 M$ {* ?2 w* [3 x% b( y/ V4) vector( const_iterator first, const_iterator last );# j4 m- k( N+ o+ V  K
( G4 W8 N9 o) T, M  ~+ N
事实上,这个构造函数应该为
& k1 B1 ~5 w6 J' [" o
0 `' z! Q& ^; ]* stemplate<typename Iter>. {: c/ w  ]# r  Y# X8 v
    vector( Iter first, Iter last );
& W. i" [: x3 n* F8 |: V* }即拷贝任意的序列 [first,last) 到 vector 中。由于 VC++6sp0 编译程序的限制, Iter 被换为 const_iterator 了。不过,碰巧 const_iterator就是 T const*,所以可以如下使用:/ m2 J$ H$ {' @$ o: s& h% [
8 `  j3 X4 M9 H" F0 `
int a[] = { 1, 2, 3, 4, 5 };
: N& K( s1 R/ f( |0 n2 |. rIntVector v5( a, a + 5 ); // {1,2,3,4,5}
1 \  ^: ?3 L2 p7 M9 SIntVector v6( v5.begin() + 2, v5.end() ); // {3,4,5}
, S, e- u/ [# O: ]5 F# z: E5. 访问 vector<> 中的元素% s2 b. ^% w" @8 G  M( ?6 B9 I0 i7 ~
以下成员函数/运算符用于访问 vector 中的一个元素:
+ A2 u: V! e$ c% b9 @1 P. g0 `* z- m" D  t1 n0 f
T& at( size_t n );& x+ ~0 R% Z; J, \
T const& at( size_t n ) const;$ [/ _, E* |, n& m; A$ J( @* `
T& operator [] ( size_t n );
1 B, `; H; H' ]% AT const& operator [] ( size_t n ) const;) w7 C  s7 W% m
T& front();, [0 r# F# c! H$ E2 N& c
T const& front() const;
" y, o  s* {' ]3 aT& back();
% m' O8 e8 r7 y/ a0 qT const& back() const;5 d& b: _8 o' B
请注意,由于 vector 是一个“值”语义的对象,所有的操作函数都必须严格保证 const 的正确性。所以,所有的元素访问方法都有 const 和非 const两个版本。
) O2 |7 x5 M# g4 t9 W8 s: P! y  Z+ U6 y
at(n) 和 operator [] (n) 都返回下标为 n 的元素的引用,他们的区别是,at() 进行下标越界检查,若发现越界,抛出 range_error 异常,operator[]不进行下标检查。
; O/ c3 b6 q  l7 q6 d& ~- N) u8 x2 C" j5 I+ s5 ?( F- M( [+ k
front() 返回下标为 0 的元素的引用,back() 返回最后一个元素的引用。
% H$ V& p8 B4 t/ Q0 ]
; D+ R* x  P: A9 _int a[] = { 4, 1, 4, 1, 5, 8 };
( @$ P+ e( D8 UIntVector v( a, a + 6 );) e  A' z, T! G
// 使用 front(), back():1 z" e( k& }. {+ a8 R% N
v.front() = 3;8 m4 a$ F. n4 v* U
v.back() = 9;/ M2 g5 J( i2 G1 e: i; s' Z9 r
// 使用 operator [] ():
: ~$ U  b( H) h8 v* X" b5 mfor( size_t i = 0; i < v.size(); ++i ). q& ]. B( ?, x$ Y) z0 U9 }
::std::cout << v[i] << '\n';
+ X0 d3 b% n. K  U) o% `+ A6. ::std::vector<> 的存储管理
, ^! n9 ?! V5 w( O以下成员函数用于存储管理:/ H  |, s# a% @. s+ W0 w

% U3 r$ l+ @7 f% ]void reserve( size_t n );# F) c# J5 i" i5 s' M& _' A3 r6 b  @
size_t capacity() const;
0 p  X% H% d8 y4 j2 Wvoid resize( size_t n, T t=T() );* K: h* t7 l3 m) ^* G* ]
void clear();
8 M! d# a: v2 Bsize_t size() const;* V) d8 Q& T( y. g
bool empty() const { return size() == 0; }: U7 d+ N3 v, e3 B+ Q% F' c( L
size_t max_size() const;
3 W% k7 R5 b$ y) Z
, {* w2 B- L1 h另外,push_back(), insert() 等也涉及到存储管理,后面另行介绍。
- `* L4 ~7 _$ t. Q' }* }4 S: p4 c, d" Q3 c, S
1) max_size()* N/ L0 H# F" f' ?; ~: L" M

) v. J  t1 ^' x8 _. D返回 vector<T> 理论上可以装的最多 T 的个数。这只是一个理论上的数字, 大概是 4GB/sizeof(T),没有多大实用价值。在程序中不要用。
* Q2 a( f% l6 |; l3 Y6 m. N- k
2) size()
- r; u) x( x" R( D9 e7 b: U( Y6 a& c, o8 a
返回 vector<T> 中实际装的 T 的个数。相当于 CArray<>::GetSize()。  K3 M- S; {# C) f: V
* s# x7 Q+ `$ ~. k8 ^( v: v
3) empty()
& n! c" t" p  T) D# O$ |) d8 h" c+ ]5 }
如果 vector<T> 中没有任何 T 对象,返回 true。也就是返回 size() == 0。- S$ v5 E1 f# |: H/ O4 L% F
3 k2 B, X* D4 e7 E. V0 k
4) clear();
, V' |* A/ f# I# }; M
: L6 `( Z8 u7 P* t清除 vector<T> 中的所有 T 对象。执行后 empty() 返回 true。大致相当于 resize(0),但不要求 T 可被缺省构造。相当于 CArray<>::RemoveAll()。; P' F( H4 ~( W" O8 j: w  T2 ~
" V2 A5 n1 \& \5 H) ]
5) resize( size_t n, T t = T() );
, T# |) F* s8 _6 x' c, v) ~. A8 Q; i5 [& U- ^  y. y
将 vector 中的元素个数设置为 n,n 可以大于 size() 也可以小于 size。如果 n 小于 size(),那么 vector 中下标为 n..size()-1 的元素都将被解构。如果 n > size(),那么将在 vector 的后面新增加/ h/ c7 d( z9 R" s& n8 }% S
n - size() 个相同的元素 t。在增大 vector 时,可能发生存储再次分配。总之,调用resize( n, t ) 后,(size() == n) 成立。$ a; h0 M# _: |7 H  w

0 _6 R* k3 _3 |5 j' S4 F请注意,如果调用 resize( n ) 不带参数 t ,那么 T 必须可以缺省构造。
5 q, o9 [6 Q! N; [$ a, ]. H) v# g1 q- h$ X1 w- g' t8 X
6) reserve( size_t n );1 i1 ?; _* w8 [" S) C4 z* E1 ^
0 I- n: g' a( j5 g8 ?, H: G/ [
事先分配至少可以保存 n 个 T 对象的空间。调用后 (capacity() >= n)成立。  k# c- j3 @& |0 B  K

. Y+ c1 Q" z% }0 ]$ v7) capacity();
1 q' c# b9 v" y7 F9 G( L6 F
+ m. a4 r! E$ ?) M% b返回已经分配的存储空间够容纳的 T 类型对象的个数。后续的增加元素操作(如 push_back(), insert())如果增加元素后 vector 中的总元素个数不超过 capacity(),那么 vector 的实现保证不重新分配存储空间。
9 |" A. e0 r, r( X9 t# p: m
9 w- n2 a. I. c1 w- Rvector 管理的动态存储空间是连续的。执行操作
9 n8 k' I, u  ~) K  w# \+ I
* S) N3 F2 q& O; P6 ?) \  J0 JIntVector v(7, 1); // seven ones.
! u; X$ J1 k7 Dv.reserve( 12 );
3 o$ H% S  S& W% a, h后,v 的状态可以用下图表示:
7 i* E# ^% p9 N+ W9 x; B& ^( f- X; E/ u
5 W: G' k7 p2 M$ O. f% m /--size()---\! H+ x, Z' H4 G3 `+ `
|1|1|1|1|1|1|1|-|-|-|-|-|
& j8 c2 j7 I9 D8 y& n/ ~. z; _ \--capacity()---------/
6 y2 M/ ~/ f  j& C- d! N其中,1 是已经构造的 int 类型的对象,- 是可以构造一个 int 类型的对象,但还没有构造的原始空间。再执行6 t8 _% S7 `4 d, \& H
; Y$ D& d6 O9 c3 F, _
v.push_back( 2 );
! b' X" P" [  m: E3 R' ?v.push_back( 3 );4 H9 d4 U6 [/ D' [/ _/ }* i
后,v 的状态可用下图表示:5 f, V0 y* U4 f% @& f% d( L$ v
: U$ V: `9 {  \% G
/----size()-----\
3 w7 [4 D0 K$ j. x0 w|1|1|1|1|1|1|1|2|3|-|-|-|
3 r4 f. X; o# Q) |, H1 e8 J \----capacity()-------/+ L$ \( K1 W' t
执行 resize( 11, 4 ); 后:: ~" d+ d4 H( {( `! }
/ X1 V3 R6 W1 m
/----size()---------\# F; N, E. o- i* m7 o
|1|1|1|1|1|1|1|2|3|4|4|-|' `8 j; ~5 g7 t- Y: K9 O( P7 @
\----capacity()-------/
( Z) N8 U2 X: Gcapacity() >= size() 总是成立的。对于下标为 [size()..capacity()-1]的未构造对象的存储空间,是不可以访问的:
" C% W' B& Q, L; S3 s8 R9 A+ \, ^( }) o. w( m# f9 j
v[11] = 5; // undefined behavior - anything can happen.8 X; y* v1 P* L9 t  |# |9 R
7. 添加元素到 vector 中' v6 m& C5 Z4 Z( [/ a; G
下列操作添加元素到 vector 中,并可能引起存储分配:
5 u  {5 H4 E) @
, `1 T0 a* V1 y6 w; Y+ i  Vvoid push_back( T const& t );
$ F" Q  J5 d/ V4 avoid insert( iterator pos, T const& t=T() );
- K) B% }* ~) u  w5 cvoid insert( iterator pos, size_t n, T const& t );
/ ?9 v$ ~8 u( z. N; @template<typename Iter>
" [6 }' ~1 g. h  D9 Q4 q    void insert( iterator pos, Iter first, Iter last );* j+ }7 H# t0 k" M% k/ b' N
push_back() 是把一个元素添加到 vector 的末尾。insert() 是把一个 t,或 n 个 t,或从 first 开始到 last 结束的一个序列插入到 pos 指示的位置之前。( W% a* V" h" G' Q! d: B
2 g2 t' a( @9 e& x7 l- E+ m: D
当插入元素后 size() 将会大于 capacity() 时,将引起自动存储分配。vector 将会分配一个比需要的存储区大若干倍(通常是1.5到2)的新的存储区,把老的元素拷贝过去,同时完成添加或插入,然后释放老的存储区。$ B" d, ?( R( }2 u

, C' d  X" a# P: A! H2 J" C: d这就是说,vector 自动存储分配的空间大小是指数式增长的,这可以保证多次添加元素到 vector 中时,平均用时是接近于常数的。1 |( Q7 c0 ]  t% Q  X0 q
% i' C3 h0 f2 o' b" ^" ]
IntVector v;( l* |- y& Z* W
   $ Y5 a- s$ }3 D; j. O
// add 0, 1, ..., 99 to v:
* K1 f/ o8 Q4 x' k. c& q2 |for( int i = 0; i < 100; ++i )
! Y7 |! v7 D, c! z; Ov.push_back( i );9 j2 S% D, L+ j7 P& M
   5 h8 y' M* k% W( J
// append 9, 8, 7,..., 0 to the end:( S7 @9 b& Q3 ^2 A% z! Q4 b
int a[] = { 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 };" {0 U$ I2 ?1 k  @
v.insert( v.end(), a, a + 10 );
9 T0 p; r# x: A/ N8. 删除元素
0 G2 K; n& e* q& P下列成员函数完成元素删除:
% U2 _' z6 Y, X" o# o  d9 K5 N8 J) Y
void erase( iterator );
; N& F0 Z, s, W1 @! c2 S- dvoid erase( iterator first, iterator last );
+ O5 o& s5 a) T# D3 \  v8 cvoid pop_back();
4 D) H. G6 b, V' u5 C8 uvoid clear();
; ?$ v: U) `* ~$ e9 b6 R6 p/ _这些函数分别删除一个,一串,最后一个,或全部元素。
- r9 e. p$ k) k2 K6 F: a  n
3 _, {5 X2 n0 s3 R0 L  a# NIntVector v;9 i3 X  J2 o7 b2 R
for( int i = 0; i < 100; ++i )" w- h# ^2 _5 b3 L. B
    v.push_back( i );8 M2 V: L+ t! G6 e3 e/ N) K* d
   
) N( M4 u: P( j3 P// 删除 50, 51, ..., 89:* ]/ D1 V) K! [; M
v.erase( v.begin() + 50, v.end() - 10 );! w4 ^3 u: F; O! P0 m4 r" g
   
: f3 m0 T. e* m9 `+ |: w" H  a3 ^' e( m// 删除 49, 48:
: U$ I' K/ i3 T+ a3 b5 y# m; d$ ~v.pop_back();
, B3 m: k9 \$ N6 Q( h9 l6 z$ t) r  Nv.pop_back();8 M) a9 c0 b* s! b$ n
   % \) H' |' m9 Y0 e: X$ c
// 全部删除:* |) y. ~' t; h5 @( K
v.clear();
" h( ]) A+ v3 t( q注意,删除操作不会引起存储分配,因此 capacity() 不变。
3 m) |$ F% d# q' s# u! `4 }- u' q- A, S
9. 作为序列访问 vector 中的元素; E6 P; Z7 l3 @" Z3 `
序列(sequence)在 STL 中是一个非常重要的概念,所有的容器类型和算法都涉及到,而且所有的算法都是建立在“序列”这个概念之上的。
1 e, _  w1 x2 U0 H2 Z/ v+ c
: I  a# W4 z) V“序列”是一个线性结构,由一个指示其起始和一个指示结束的叠代子(iterator)来决定。如果 first 和 last 是某种类型的叠代子,那么经常用[first, last) 来表示一个序列。注意,first 指向的元素是这个序列的一个元素,而 last 指示的是这个序列最后一个元素之后的位置,可能根本没有元素可以访问。这种半闭半开的区间表示是整个 C++ 标准中的约定,而且确实可以简化程序。! q: ~% r- A7 f
2 z( c1 [5 J8 K& K- E, e! r
叠代子是传统的 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 的要求。
- {/ O1 [5 g3 i' ], f" R1 x) p/ [
# s6 G5 w& d3 I; J; B8 ]3 A. ]7 avector<> 中定义了以下函数用于获取被控制(管理的)序列(动态数组)的各种叠代子:
" e% ~2 _& |7 P
1 N4 f1 e; T2 \6 a6 [) Siterator begin();
  J/ \4 f/ b# ~% O) s- [: _, Giterator end();
$ B# B# A' Y& X2 aconst_iterator begin() const;
& Z3 ?& l6 y% p* y7 k; gconst_iterator end() const;4 t! t* F3 ^! Z# a- @
reverse_iterator rbegin();. _  O2 U4 T8 N- t6 c4 p0 G
reverse_iterator rend();% S) S: p( H" Z7 T, `: }
const_reverse_iterator rbegin() const;
! K# d- d! T5 l3 }. W/ B2 i% I) Gconst_reverse_iterator rend() const;) |) k2 e: s. F4 q7 V! Y
这里我们不讨论叠代子的一般概念,只举几个 random access iterator 的例子:8 ]8 P; d2 A1 E. l; Z

; G5 B1 l2 p8 q# `- e) Y8 Aint a[] = { 1, 2, 3, 4, 5, 6 };
3 ]" k/ ]  E# ^: m; |/ A$ S$ J5 J[a, a + 6) 是一个随机访问序列,指示了 a[] 中的所有元素。这里叠代子的类型为 int*。
% {- A& y( g- U2 g  `1 W  M
2 [% j, c9 W/ m. f" H6 e: G6 ?2 p1 b[a + 2, a + 4) 也是一个序列,指示了 a[] 中的 3, 4 两个元素。叠代子的类型仍然是 int*。( f1 T) n+ U! a3 G

& G  G) C6 t9 [' pIntVector v( 100, 1 ); // 100 个 1。
# w; l: ?3 k, k' k  }& B2 n4 u[v.begin(), v.end()) 是一个随机访问序列,指示了 v 中的所有元素,叠代子的类型是 IntVector::iterator。
) X5 V' P, _) {: W! \+ I7 V7 U0 K* I9 |) g% I
[v.begin() + 10, v.end() - 20 ) 也是一个随机访问序列,指的是 v 中除了头 10 个和尾 20 个元素外的其它元素。; j# [: Y9 ~& x7 O! Q) W' a# x
1 Y' R/ T" @  q  \0 C: E; K
[v.rbegin(), v.rend() ) 是一个随机访问序列,指的是 v 中的所有元素,但与 [v.begin(), v.end() ) 不同,这个序列是从尾到头遍历所有元素。
* @+ U% r2 N4 r; }
8 N5 |# r; L1 h5 |/ t3 C8 l( I[v.rbegin() + 20, v.rend() - 10) 与 [v.begin() + 10, v.end() - 20 )指示的元素相同,但遍历顺序相反。, L+ ^8 \; G2 ?1 f/ p  J5 v: M& ?

' B0 i  v* {" T  n: G8 |下图是有十个元素的 vector 的 begin()/end()/rbegin()/end() 的示意:
; M9 U) W" x! A% P9 B) R* N; R& w/ |: C& S6 ^* F! r1 I
begin() ----------> end()% k1 S) y5 W0 h' R7 W7 t6 ^/ s; X
  |                   |
/ B# ]+ @8 q/ Y: s1 a  v                   v
; `% P$ g' ?2 r3 p/ Y |0|1|2|3|4|5|6|7|8|9|, y2 E( U; Y% z# `2 ]
^                   ^1 s4 T/ a1 p, E$ l/ S7 M2 h
|                   |3 S/ \- [. i% a' I- y  g
rend() <---------- rbegin()* a- i# o6 C1 k' g
   
, D4 d* T# T& }" lIntVector v;' ?" S" n% v& W, ]$ Y4 W
for( int i = 0; i < 10; ++i )
# ^  o; d) V* p- |2 nv.push_back( i );) N$ K/ v$ h( z  @
   , R* L6 ]" |6 c& B; I
// print 0, 1, 2, ..., 9:! `" c" A% T! C! M
for( IntVector::iterator i = v.begin(); i != v.end(); ++i )3 S% `9 P# Q8 J6 r
::std::cout << *i << '\n';2 C7 h" ?* s% Z: r) `7 g
   1 y& l" S. N6 K( }- \
// print 9, 8, ..., 0:# A5 Q% g3 y' E" A4 X8 C
for( IntVector::reverse_iterator i = v.rbegin(); i != v.rend(); ++i ), b' a, U3 s& Q. r2 v3 ~; v/ t
::std::cout << *i << '\n';
1 N6 j) l7 \9 a6 }  A$ F# h除了使用 begin()/end()/rbegin()/rend() 来遍历 vector 中的元素外,由于 vector 管理的空间是连续的,因此可以直接取地址进行处理:
" N% P0 }$ `1 w  E; ^- J& U5 M+ ^* k  v
::std::vector<HANDLE> handles;
% e2 m  }+ e' L* D+ |; _  fhandles.push_back( handle1 );; {9 M& D$ r5 z+ @0 q" A
handles.push_back( handle2 );- W/ L. x! h. G" g  D+ t

5 y: M: C$ K' h0 l2 x2 J6 t1 e0 p- G6 O: c- a% `! Z- s
::WaitForMultipleObjects(handles.size(), &handles[0],TRUE, INFINITE);% J' x0 h0 [' M) ]7 m" J' E
这在与 C 库函数接口时尤其有用。
5 h3 _; L( [! n7 o+ K) O
# L2 P8 _" g5 a- J- ~- o10. 赋值和交换# j5 L0 U4 s* P( y
vector<> 是可以赋值的,这也是一般的“值”类型必须提供的操作:7 G: r3 f  |4 Q& u, I1 O/ z
) u' o# x# z& ^: E+ d! u
IntVector v( 100, 123 );( e$ D- ~! Z' `" q; K
IntVector v1;* u' c1 y( {$ D
v1 = v;1 B9 Q, A" p" F3 V. C/ q
vector 另外还提供了/ p. X5 j! N+ H5 p
" r( z& v0 h* y3 n% l! N  ?
template<typename Iter>
# n5 Q% }1 S2 B! ?# a: Yvoid assign( Iter first, Iter last );& |$ w' s5 {5 a; B- G6 a! L
void assign( size_t n, T const& t = T() );
, J% }6 F9 f+ x0 B! _5 h) ~用于赋值:
( M) S" U0 J  G* ?2 z) N
3 p( U8 |+ u+ {/ O6 G* g6 X" Z; Gint a[] = { 1, 3, 5, 7 };
+ I( {8 s; d( [& Bv.assign( a, a + 4 ); // v 将包含 1, 3, 5, 7.
. g6 K/ b5 W0 \( J$ Av.assign( 100 ); // 100 个 0。
2 N: Z* |7 U) N; ]9 D% e" p) m还有一个很重要的操作:( z$ Z5 ^& C9 w4 x3 o; Q) U

# v6 g4 S) }6 L9 i' J% }void swap( vector& v ) throw();9 N; ?$ N; h. |7 N
用于交换两个同类型的 vector 的值。它的特点是快速(只需要交换内部的三个指针),不产生异常。这在写一些保证异常安全的程序时非常有用。
! g; \$ ?1 {' o2 U. t
! f  W: _& y1 {# k2 b# q事实上,swap() 基本上已经被当作类似于 operator=() 的一个“值”类型应该提供的基本操作,::std::swap() 也应该为用户定义的类型进行特例化,调用相应的类的成员 swap() 函数:
, O4 N5 @7 k9 g9 S2 B3 {3 H, Y/ W& @' y
struct MyVal. B6 c4 k4 b7 L& O* E
{7 ~+ K4 `' j  r+ O9 w% W
  // blah blah.
4 g6 G1 A, h! G' c& ?3 ~  void swap( MyVal& ) throw();# V) Y8 k6 F' v( o% x, _
};
' E2 [  K2 K" \   
- }2 _+ ?! c/ o5 |namespace std {
+ R" `5 Y" u5 L) y0 l" T  template<>
, b$ m0 ?8 D3 Q# z3 F+ d    void swap( MyVal& a, MyVal& b ): }+ v# Y) q- e4 R
    { a.swap( b ); }6 e8 X3 K& t( ]# x2 b
}
# w  U, c5 a! Q1 f关于 swap(),值得专文讨论。这里我们只指出,vector<T>::swap() 是快速的,不抛出异常的,很有价值。
; ?; `* \/ `! {. i8 V% Y. E
- W4 n1 J* k. j; o4 {# X/ w11. 使用 vector 时的存储管理策略4 E1 @" l8 G" j7 K
从前面的介绍中可以看到,vector 的自动存储分配是指数式的增加存储空间,而且永不缩小已经分配的空间。这在大多数情况下是合适的。 如果应用程序事先知道要用到的元素个数,可以先调用 reserve() 来保留(分配)空间,这样可以避免以后增加元素时不必要的重新分配和元素拷贝:: s7 c7 g+ l2 |5 J8 @
1 {4 e3 c" U* _- q' x+ ]! `
IntVector v;1 i, T( J# i! [1 q) L# A
v.reserve( 100 );2 B; h" i6 Q. |, B
for( int i = 0; i < 100; ++i )
: U5 N3 C- N7 c$ e    v.push_back( i );
3 P0 ?7 o, }  }" v1 A请注意,reserve() 和 resize() 是本质上完全不同的。reserve(n) 保留的是未使用而能够使用的原始空间,而 resize(n) 是真的创建了 n 个对象:2 p6 I9 T4 X* w

8 r( x3 p1 ~& \: B$ C  g  ]  n& v% V$ OIntVector v;/ E8 p- t6 w& ]; @9 c* m# T
v.resize( 100 ); // v 已经包含 100 个 0.
+ s  I+ l; Q+ Y, }( T* O" D* [for( int i = 0; i < 100; ++i )
( B* o6 s! N8 R$ Y7 q2 t/ m    v[i] = i; // 可以赋值  Q3 J7 u2 G% X
有时候,一个 vector 可能增长到较多个元素,然后又减少到较少的元素个数,这时,可能希望缩小 vector 分配的空间以节约内存。CArray<> 中提供了 FreeExtra(),但 vector<> 并没有提供相应的函数。这时必须进行复制:
- I& H$ v( B# ?4 h: c& X! D
- W( E2 u2 {+ W7 |1 QIntVector(v).swap( v );$ p" d2 W( e* J  |3 z: S
有一种看法认为拷贝构造函数同时也复制了capacity(),而标准中并没有很明确地指出这一点,因此更安全的方法是% i3 C( C$ }9 V1 l& Y6 {3 B
+ ?0 S& O6 f4 c$ l, m/ N2 ]  j2 n) i
IntVector(v.begin(),v.end()).swap(v);! @' \- F# O1 E/ C4 R, v: c
如果一个 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-15 01:38 , Processed in 0.019251 second(s), 15 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

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