Document directory
- 1. carray <> vs: STD: vector <>?
- 2.: STD: vector <> where?
- 3.: type definition in STD: vector <>
- 4.: STD: vector <> Construction
- 5. Access elements in the vector <>
- 6.: STD: vector <> storage management
- 7. add elements to the vector
- 8. Delete Elements
- 9. Access elements in a vector as Sequences
- 10. Assignment and exchange
- 11. Storage Management Policy when vector is used
Abstract: This article introduces the container class vector in the C ++ standard library and analyzes its advantages. It is recommended to use it in applications as the priority of dynamic arrays, instead of other class templates such as carray of MFC. Finally, the vector interface and precautions are introduced.
In some programs that use MFC, we often see many programs use carray. Due to the design problem of carray, the code that uses it becomes more complicated and the maintenance difficulty is increased. Therefore, we recommend that you use: STD: vector <> instead of carray <>.
In addition, some programs use malloc/realloc/free/new []/Delete [] to manually manage the memory. In applications, manual memory management is prone to errors. You should use objects such as: STD: vector <> to manage dynamic arrays.
As msdn has little content about: STD: vector, we will introduce it here for your reference.
It doesn't matter if you are not familiar with carray <>/Win32. There are not many places to mention them here.
1. carray <> vs: STD: vector <>?
Like: STD: vector, carray is a template class used to manage dynamic arrays of any type of objects. All of them release the managed dynamic memory during deconstruct. Therefore, it can be used instead of manual dynamic array management.
However, carray was designed many years ago (in the VC ++ 2.0 era) before C ++ standardization. At that time, it was designed for C ++ programming and object-oriented programming, the knowledge of template programming and other technologies is seriously insufficient, especially the incorrect belief and propaganda on object-oriented technology at that time, resulting in a major error in carray <> design.
After the standardization of C ++ language (1998) and after the birth of VC ++ 6.0, the standard: STD: vector <> template is provided, basically, it is better than carray in any aspect <>. Microsoft keeps carray because it needs to support old programs, but it apparently does not intend to develop it according to new ideas (at least provide operator = (carray const &) ).
In summary, carray and STD: Vector have the following differences:
1) carray <> is in MFC, And: STD: vector <> exists in any standard C ++ implementation. Therefore, you are familiar with carray <> and can only use it in MFC. If you are familiar with: STD: vector <>, you can use it in any c ++ compiler on any platform. Using standard components also helps others understand your program. . Carray <> inherits the cobject and only implements serialization. This is inappropriate and violates "you don't pay for what you don't use. "c ++ design principles. : STD: vector <> does not inherit anything, but manages a dynamic array.
2) carray <> is not an appropriate value type. For example, the following operations are invalid:
CArray<int,int> a;CArray<int,int> b(a); // error, must use Copy().b = a; // error, must use Copy().b == a; // error, you must write your own.b < a; // error, you must write your own.
Contrary to carray <>,: STD: vector <> is a carefully designed value type, which can be copied, constructed, and assigned. If t is comparable, then: STD: vector <t> is automatically comparable.
In addition, four special member functions are involved;
T (); // default constructor )~ T (); // destructor T (t const &); // copy the constructor T & operator = (T const &); // copy the value assignment function
If carray () is used as the member variable of T, the last two of the above four special functions cannot be automatically generated and must be manually written:
struct T{ T() {} T( T const& t ) { a_.Copy( t.a_ ); i_ = t.i_; d_ = t.d_; s_ = t.s_; } T& operator = ( T const& t ) { if( this != &t ) { a_.Copy( t.a_ ); i_ = t.i_; d_ = t.d_; s_ = t.s_; } return *this; }private: CArray<int,int> a_; int i_; double d_; ::std::string s_;};
If: STD: vector <>:
struct T{private: ::std::vector<int> a_; int i_; double d_; ::std::string s_;};
The three special member functions listed above do not need to be written. The advantage is obvious: When you increase or decrease T member variables, you do not have
T (t const &) and operator =.
3) There is no ready-made algorithm to operate carray, while most of the standard algorithms in Standard C ++ can be directly stored in
: STD: vector <>. For example:
Static int const init_vals [] = {3, 1, 4, 1, 6, 9}; vector <int> A (init_vals, init_vals + 6); * Find (. begin (),. end (), 6) = 5; // change 6 to 5 sort (. begin (),. end (); // sort.
It can be said that the main design error of carray <> is to design a simple "value" type thing into a difficult "object" type. All the good features of "value" are lost, but what about the derived classes inherited from carray <>?
Cbytearray and other problems are the same as those of carray <> (for example, cptrarray, never used ).
Similarly, other MFC iner templates, such as cmap and clist, have similar problems and should be used
: STD: Map <>,: STD: List <>.
2.: STD: vector <> where?
: STD: vector <> defined in the header file <vector>:
(Note: none of the Standard C ++ header files are available. h suffix,. H files are compatible with C, or support old nonstandard things, such as <iostream. h> .)
Namespace STD {template <typename T, typename A = Allocator <t> struct vector {// details will be discussed later}; Template <typename t, typename A> bool operator = (vector <t, a> const & A, vector <t, a> const & B); Template <typename T, typename A> bool Operator! = (Vector <t, a> const & A, vector <t, a> const & B); Template <typename T, typename A> bool operator <(vector <t, a> const & A, vector <t, a> const & B); Template <typename T, typename A> bool operator >=( vector <t, a> const &, vector <t, a> const & B); Template <typename T, typename A> bool operator> (vector <t, a> const & A, vector <t, a> const & B); Template <typename T, typename A> bool operator >=( vector <t, a> const & A, vector <t, a> const & B );}
Vector <> is defined in namespace STD. In order to reduce the number of keys used, a type definition is usually used to shorten the type name:
#include <vector>typedef ::std::vector<int> IntVector;IntVector a;IntVector b( a );IntVector c;c = b;assert( a == c );
Note that <vector> defines the comparison functions of the six vectors <t, a>. These functions are instantiated only when they are actually used, and T is required to provide operator = () and operator <().
In addition, a = alloctor <t>: provides a user-defined storage management class. This parameter is rarely used, and has problems in the implementation of VC ++ 6 and cannot be used. Therefore, we will ignore this part of content in the following discussion.
3.: type definition in STD: vector <>
Vector <> defines some types. The following lists only common types:
typedef T value_type;typedef T0 iterator;typedef T1 const_iterator;typedef T2 reverse_iterator;typedef T3 const_reverse_iterator;
Value_type is the element type of vector <t>, that is, T. It is useful when writing common algorithms to process arbitrary types of vectors <> or other container types.
Iterator/const_iterator is an unknown type defined by the implementation of two vectors. It is used to access elements in the vector, similar to the T */T const * pointer, their difference is that one element pointing to can be modified, and the other can only read:
typedef ::std::vector<int> IntVector;IntVector::iterator iter;IntVector::const_iterator c_iter;// ...++iter; iter++; // ok: increment, post-increment.--iter; iter--; // ok: decrement, post-decrement.++c_iter; c_iter++; // ok: increment, post-increment.--c_iter; c_iter--; // ok: decrement, post-decrement.*iter = 123; // ok.int k = *iter; // ok.k = *--c_iter; // ok.*c_iter = k; // error.c_iter = iter; // ok: iterator is convertible to const_iterator.iter = c_iter; // error: can't convert const_iterator to iterator.
In use, iterator/const_iterator and T */T const * are basically the same. In fact, some implementations of vector <> Use T */T const * to implement iterator/const_iterator, however, you cannot regard iterator/const_iterator as true T */T const *:
T* p = iter; // may fail to compile.T const* q = c_iter; // may fail to compile.
Reverse_iterator/const_reverse_iterator is similar to iterator/const_iterator, but accesses elements in the vector in reverse order (from the end to the header.
Various iterator are of special significance in STL, but we will not introduce them here. As long as you understand that iterator can access the elements in the vector, it is equivalent to a pointer to indicate the position.
4.: STD: vector <> Construction
Vector <> provides the following constructor: (ignore the Allocator parameter)
vector();vector( size_t n, T const t=T() );vector( vector const & );vector( const_iterator first, const_iterator last );
1) vector ();
Construct an empty vector without any elements.
Intvector V1; // an empty integer vector.
2) vector (size_t N, t const T = T ());
Construct a vector composed of N identical elements T. If t is not given, T () will be used as the default value:
Intvector V2 (100,123 4); // 100 1234 100. intvector V3 (100); // 0.
3) vector (vector const & other );
Copy the constructor and copy the content in other:
Intvector V4 (V2); // 100 pieces 1234.
4) vector (const_iterator first, const_iterator last );
In fact, this constructor should be
template<typename Iter> vector( Iter first, Iter last );
That is, copy any sequence [first, last) to the vector. Due to the limitations of the VC ++ 6sp0 Compilation Program, ITER is replaced with const_iterator. However, const_iterator is t const *, so it can be used as follows:
int a[] = { 1, 2, 3, 4, 5 };IntVector v5( a, a + 5 ); // {1,2,3,4,5}IntVector v6( v5.begin() + 2, v5.end() ); // {3,4,5}
5. Access elements in the vector <>
The following member functions/operators are used to access an element in a vector:
T& at( size_t n );T const& at( size_t n ) const;T& operator [] ( size_t n );T const& operator [] ( size_t n ) const;T& front();T const& front() const;T& back();T const& back() const;
Note that, because vector is a "value" semantic object, all operation functions must strictly ensure the correctness of Const. Therefore, all element access methods have two versions: const and non-Const.
At (N) and operator [] (n) both return references to the element whose subscript is n. the difference between them is that at () checks the subscript out of bounds, A range_error error is thrown. OPERATOR [] does not perform subscript check.
Front () returns the reference of the element whose subscript is 0, back () returns the reference of the last element.
Int A [] = {4, 1, 4, 1, 5, 8}; intvector V (A, A + 6); // use front (), back (): v. front () = 3; V. back () = 9; // use operator [] (): For (size_t I = 0; I <v. size (); ++ I): STD: cout <V [I] <'/N ';
6.: STD: vector <> storage management
The following member functions are used for storage management:
void reserve( size_t n );size_t capacity() const;void resize( size_t n, T t=T() );void clear();size_t size() const;bool empty() const { return size() == 0; }size_t max_size() const;
In addition, push_back () and insert () also involve storage management, which will be further described later.
1) max_size ()
Returns the number of TB that can be theoretically loaded by vector <t>. This is just a theoretical number, which is about 4 GB/sizeof (T) and has little practical value. Do not use it in programs.
2) size ()
Returns the number of actually loaded t in vector <t>. It is equivalent to carray <>:: getsize ().
3) Empty ()
Returns true if no t object exists in the vector <t>. That is, the returned size () = 0.
4) Clear ();
Clears all T objects in the vector <t>. Empty () returns true after execution. It is roughly equivalent to resize (0), but t is not required to be constructed by default. It is equivalent to carray <>:: removeall ().
5) resize (size_t N, t = T ());
Set the number of elements in the vector to N. N can be greater than or less than size. If n is smaller than size (), all elements labeled N.. Size ()-1 in the vector will be reconstructed. If n> size (), it will be added after the vector.
N-size () same element T. When adding a vector, storage may be re-allocated. In short, after resize (n, T) is called, (SIZE () = N) is established.
Note that if you call resize (n) without the t parameter, T must be constructed by default.
6) Reserve (size_t N );
Allocate at least n t objects in advance. After the call (capacity ()> = N) is established.
7) Capacity ();
Returns the number of T-type objects with sufficient storage space. Subsequent adding element operations (such as push_back () and insert (). If the total number of elements in the vector does not exceed capacity (), therefore, the implementation of vector ensures that the storage space is not re-allocated.
The dynamic storage space managed by vector is continuous. Perform operations
IntVector v(7, 1); // seven ones.v.reserve( 12 );
The V status can be expressed as follows:
/--size()---/|1|1|1|1|1|1|1|-|-|-|-|-| /--capacity()---------/
Among them, 1 is a constructed int type object,-is an int type object that can be constructed, but there is no original space yet constructed. Run again
v.push_back( 2 );v.push_back( 3 );
The V status can be expressed as follows:
/----size()-----/|1|1|1|1|1|1|1|2|3|-|-|-| /----capacity()-------/
Execute resize (11, 4); and then:
/----size()---------/|1|1|1|1|1|1|1|2|3|4|4|-| /----capacity()-------/
Capacity ()> = size () is always true. The subscript is [size ()..Capacity()-1] the bucket of the unconstructed object cannot be accessed:
v[11] = 5; // undefined behavior - anything can happen.
7. add elements to the vector
The following operations add elements to the vector and may cause storage allocation:
void push_back( T const& t );void insert( iterator pos, T const& t=T() );void insert( iterator pos, size_t n, T const& t );template<typename Iter> void insert( iterator pos, Iter first, Iter last );
Push_back () is to add an element to the end of the vector. Insert () is to insert a t, n t, or a sequence from first to last to the position indicated by the POS.
When the size () of the inserted element is greater than capacity (), automatic storage allocation is triggered. Vector will allocate a new storage area several times larger than the required storage area (usually between 1.5 and 2), copy the old elements, and add or insert them at the same time, then release the old storage zone.
That is to say, the size of the Space automatically stored by the vector increases exponentially, which ensures that when elements are added to the vector multiple times, the mean time is close to the constant.
IntVector v; // add 0, 1, ..., 99 to v:for( int i = 0; i < 100; ++i )v.push_back( i ); // append 9, 8, 7,..., 0 to the end:int a[] = { 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 };v.insert( v.end(), a, a + 10 );
8. Delete Elements
The following member functions complete element deletion:
void erase( iterator );void erase( iterator first, iterator last );void pop_back();void clear();
These functions delete one, one string, one last, or all elements respectively.
Intvector V; For (INT I = 0; I <100; ++ I) v. push_back (I); // delete 50, 51 ,..., 89: v. erase (v. begin () + 50, V. end ()-10); // Delete 49, 48: v. pop_back (); V. pop_back (); // Delete All: v. clear ();
Note that the delete operation does not cause storage allocation, so the capacity () remains unchanged.
9. Access elements in a vector as Sequences
Sequence is a very important concept in STL. All container types and algorithms are involved, and all algorithms are built on the concept of "sequence.
A sequence is a linear structure determined by an iterator indicating its start and end. If first and last are some types of child substitutes, [first, last) is often used to represent a sequence. Note that first points to an element of the sequence, and last indicates the position after the last element of the sequence, which may not be accessible at all. This semi-closed and semi-open interval represents the entire c ++ standard convention, and can indeed simplify the program.
The stacked child represents the abstraction and further classification of pointers in traditional C/C ++. In C ++, iterator is divided into five categories: Input iterator, output iterator, forward iterator, bidirectional iterator, and random access iterator. Randomaccess iterator is the most powerful class, that is, it allows the most operations. The pointer type and the iterator/const_iterator/reverse_iterator of vector <>/deque <> in C ++ meet the requirements of Random Access iterator.
Vector <> defines the following functions used to obtain stacks of controlled (managed) sequences (dynamic arrays:
iterator begin();iterator end();const_iterator begin() const;const_iterator end() const;reverse_iterator rbegin();reverse_iterator rend();const_reverse_iterator rbegin() const;const_reverse_iterator rend() const;
Here, we will not discuss the general concept of stacking sub-accounts. Here are just a few examples of Random Access iterator:
int a[] = { 1, 2, 3, 4, 5, 6 };
[A, A + 6) is a random access sequence, indicating all elements in. Here, the type of the stacked child is int *.
[A + 2, A + 4) is also a sequence, indicating the 3 and 4 elements in. The stacked child type is still int *.
Intvector V (100, 1); // 100 values: 1.
[V. Begin (), V. End () is a random access sequence that indicates all elements in v. the type of the stacked child is intvector: iterator.
[V. begin () + 10, V. end ()-20) is also a random access sequence. It refers to other elements except the first 10 and the last 20 elements in v.
[V. rbegin (), V. rend () is a random access sequence, which refers to all elements in V, but with [v. begin (), V. end () is different. This sequence traverses all elements from the end to the header.
[V. rbegin () + 20, V. rend ()-10) and [v. begin () + 10, V. end ()-20) indicates that the elements are the same, but the traversal order is the opposite.
It indicates the begin ()/end ()/rbegin ()/end () of the vector with ten elements:
begin() ----------> end() | | v v |0|1|2|3|4|5|6|7|8|9|^ ^| |rend() <---------- rbegin() IntVector v;for( int i = 0; i < 10; ++i )v.push_back( i ); // print 0, 1, 2, ..., 9:for( IntVector::iterator i = v.begin(); i != v.end(); ++i )::std::cout << *i << '/n'; // print 9, 8, ..., 0:for( IntVector::reverse_iterator i = v.rbegin(); i != v.rend(); ++i )::std::cout << *i << '/n';
In addition to using begin ()/end ()/rbegin ()/rend () to traverse elements in a vector, because the space managed by a vector is continuous, therefore, you can directly take the address for processing:
::std::vector<HANDLE> handles;handles.push_back( handle1 );handles.push_back( handle2 );::WaitForMultipleObjects(handles.size(), &handles[0],TRUE, INFINITE);
This is especially useful when it is used with the C library function interface.
10. Assignment and exchange
Vector <> can be assigned values, which is also a common operation required for the "value" type:
IntVector v( 100, 123 );IntVector v1;v1 = v;
Vector also provides
template<typename Iter>void assign( Iter first, Iter last );void assign( size_t n, T const& t = T() );
Used for Value assignment:
Int A [] = {1, 3, 5, 7}; V. assign (A, A + 4); // v will contain 1, 3, 5, 7. v. assign (100); // 100 0.
There is also an important operation:
void swap( vector& v ) throw();
It is used to exchange the values of two vectors of the same type. It features fast (only three internal pointers need to be exchanged) without exceptions. This is useful when writing programs to ensure exceptional security.
In fact, swap () has basically been treated as a basic operation that should be provided for a "value" type similar to operator = (),: STD: swap () the user-defined types should also be made special, and the swap () function of the corresponding class should be called:
struct MyVal{ // blah blah. void swap( MyVal& ) throw();}; namespace std { template<> void swap( MyVal& a, MyVal& b ) { a.swap( b ); }}
Swap () is worth special discussion. Here, we only point out that vector <t>: swap () is fast and valuable without throwing an exception.
11. Storage Management Policy when vector is used
From the previous introduction, we can see that the automatic storage allocation of vector increases the storage space exponentially and never decreases the allocated space. This is suitable in most cases. If the application knows the number of elements to be used in advance, it can call reserve () to reserve (allocate) space, so as to avoid unnecessary redistribution and element copy when adding elements in the future:
IntVector v;v.reserve( 100 );for( int i = 0; i < 100; ++i ) v.push_back( i );
Note that reserve () and resize () are essentially different. Reserve (n) retains the original space that is not used and can be used, while resize (n) actually creates n objects:
Intvector V; V. resize (100); // v already contains 100 0.for( int I = 0; I <100; ++ I) V [I] = I; // can be assigned a value.
Sometimes, a vector may increase to more than one element, and then reduce to a smaller number of elements. In this case, you may want to reduce the space allocated by the vector to save memory. Carray <> provides freeextra (), but vector <> does not provide the corresponding function. Copy is required:
IntVector(v).swap( v );
One idea is that the copy constructor also copies capacity (), which is not explicitly indicated in the standard. Therefore, the safer method is
IntVector(v.begin(),v.end()).swap(v);
If a vector may store a large number of elements (for example, more than 100), and cannot determine the number in advance (so it cannot call reserve ()), generally, the vector is not an appropriate data structure. You should consider using: STD: deque <>. Compared with vector <>, deque <> does not guarantee that the storage space behind it is continuous (therefore, applications in waitformultipleobjects () cannot be replaced by deque