vector Container
Vectors are a collection of objects of the same type, each with a corresponding integer index value. As with string objects, the standard library is responsible for managing the associated memory of the storage element. We refer to the vector as a container because it can contain other objects. All objects in a container must be of the same type.
Before using vectors, you must include the appropriate header files.
#include <vector>
Using Std::vector;
A vector is a class template. Templates allow programmers to write a single class or function definition that can be used on different data types. Therefore, we can define a vector that holds a string object, or a vector that holds an int value, or a vector that holds a custom class type object, such as a Sales_item object.
Declaring a type of object produced from a class template requires additional information, depending on the template. Vector, for example, must describe what type of object the vector holds, specifying the type by placing the type in angle brackets following the name of the class template:
Vector<int> Ivec; Ivec holds objects of type int
vector<sales_item> Sales_vec;//Holds Sales_items
As with other variable definitions, define a vector object to specify a type and a list of variables. The first definition above, the type is VECTOR<INT>, the type is a vector containing several types of int, and the variable is named Ivec. The second defined variable name is Sales_vec, and the element it holds is an object of type Sales_item.
Vector is not a data type, but just a class template that can be used to define any number of data types. Each of the vector types specifies the type of the element it holds. So,vector<int> and vector <string> are data types.
definition and initialization of vector objects
The vector class defines several constructors for defining and initializing vector objects. These constructors are listed in table 3-4 below:
Vector<t> v1; |
The vector holds an object of type T. The default constructor v1 is empty. |
Vector<t> v2 (v1); |
V2 is a copy of v1. |
Vector<t> v3 (n, i); |
The v3 contains n elements with a value of I. |
Vector<t> v4 (n); |
V4 contains n copies of elements that are initialized with values. |
Create an element of a certain number
To create a non-empty vector object, you must give the value of the initialization element. When a vector object is copied to another vector object, each element in the newly copied vector is initialized to a copy of the corresponding element in the original vector. However, these two vector objects must hold the same type of element:
Vector<int> IVEC1; IVEC1 holds objects of type int
vector<int> ivec2 (IVEC1);//ok:copy elements of ivec1 into ivec2
vector <string> Svec (IVEC1); Error:svec holds strings, not ints
The vector object can be initialized with the number of elements and the element value. The constructor uses the number of elements to determine the number of elements the vector object holds, and the element value specifies the initial value of each element:
Vector<int> IVEC4 (10,-1); elements, each initialized to-1
vector<string> Svec (Ten, "hi!");//strings, each initialized to "hi!"
Key concepts: Dynamic growth ofvector objects
The important attribute of vector objects (and other standard library container objects) is the ability to add elements efficiently at run time. Because vectors increase in efficiency, it is better to add elements dynamically when the element value is known. This growth pattern differs from the built-in data types in C and the data types in most other programming languages. In particular, if the reader is accustomed to the C or Java style, as vector elements are stored continuously, it may be desirable to allocate the appropriate space beforehand. But in fact, in order to achieve continuity, C + + is doing exactly the opposite. Although it is possible to pre-allocate memory for a vector object of a given number of elements, it is more efficient to initialize an empty vector object before adding the element dynamically.
Value Initialization
If the element's initialization is not given, then the standard library will provide a value-initialized (value initialized) element initializer. The initial value generated by the library is used to initialize each element in the container. The value of an element initializer depends on the data type of the element stored in the vector.
If the vector holds elements of a built-in type (such as int), the standard library creates an element initialization value with a value of 0:
Vector<string> Fvec (10); Ten elements, each initialized to 0
If a vector holds an element of a class type (such as a string), the standard library creates the element's initial value with the default constructor for that type:
Vector<string> Svec (10); Ten elements, each of an empty string
For classes with custom constructors but no default constructors, when initializing this type of vector object, the programmer cannot provide only the number of elements, but it also needs to provide the element's initial value.
The element type may be a class type that does not have any constructors defined. In this case, the standard library still produces an object with an initial value, and each member of the object is initialized with a value.
operation of the vector
The Vector Standard library provides many operations similar to string objects, and the following table lists some of the most important vector operations.
V.empty () |
Returns true if V is null, otherwise false is returned. |
V.size () |
Returns the number of elements in V. |
V.push_back (t) |
Adds an element with a value of t at the end of V. |
V[n] |
Returns an element of position N in v. |
V1 = V2 |
Replace the V1 element with a copy of the element in V2. |
V1 = = V2 |
Returns true if V1 is equal to V2. |
! =, <, <=, >= |
Keep these operators in their customary meaning. |
size of the vector object
The empty and size operations are similar to related operations of type String. The member function size returns the value of the size_type defined by the corresponding vector class.
When you use the Size_type type, you must indicate where the type is defined. The vector type always includes the element type of the vector:
Vector<int>::size_type//OK
vector::size_type//Error
Adding elements to vectors
The push_back () operation takes an element value and adds it to the back of the vector object as a new element, that is, "insert (push)" to the "back" of the vector object:
Read words from the standard input and store them as elements in a vector
string word;
vector<string> text; Empty vector
while (cin >> word) {
text.push_back (word);//append Word to Text
}
The loop reads a series of string objects from the standard input, appending them to the back of the vector object. First, define an empty vector object text. A new element is added to the vector object once per loop, and the word value read from the input is assigned to the element. When the loop ends, the text contains all the elements that are read in.
Subscript operation of vectors
Objects in the vector are not named and can be accessed by the position of the objects in the vector. You typically use the subscript operator to get an element. The subscript operation of a vector is similar to a string-type subscript operation.
The vector's subscript operator takes a value and returns the element at that corresponding position in the vector. The position of the vector element starts from 0. The following example uses a For loop to reset each element value in the vector to 0:
Reset the elements in the vector to zero for
(vector<int>::size_type IX = 0; IX! = Ivec.size (); ++ix)
ive C[ix] = 0;
Like the string subscript operator, the vector subscript operation results in an lvalue, so the write can be implemented as it did in the loop body. In addition, similar to the subscript operation of a String object, the Size_type type is used as the type of vector subscript.
In the example above, the for loop will execute correctly even if Ivec is empty. Ivec is empty, call size returns 0, and the test in for is compared with IX and 0. When the first loop occurs, the condition test fails and the For loop body does not execute once because IX itself is 0.
Key Concepts : Secure generic programming
A C + + programmer accustomed to C or Java programming may find it difficult to understand that the for loop judgment condition uses! = instead of < to test whether the vector subscript value is out of bounds. C programmers are hard to understand also, the previous example did not call the size member function and save its return value before the for loop, but instead called the size member function in the For statement header. C + + programmers are accustomed to using! = rather than < to write loop-judging conditions.
Calling the size member function without saving the value it returns is also not required in this example, but it reflects a good programming habit. In C + +, some data structures, such as vectors, can grow dynamically. In the example above, the loop only needs to read the elements without adding new elements. However, the loop can easily add new elements, and if you do add new elements, then it is problematic to test the saved size value as the end condition of the loop because the newly added element is not counted. So we tend to test the current value of size in each loop instead of storing a copy of the size value when entering the loop.
We know that some functions in C + + can be declared as inline (inline) functions. Instead of making actual function calls, the compiler expands the code directly when it encounters an inline function. A small library function like size is almost defined as an inline function, so the cost of invoking it at the time of each loop is relatively small.
Subscript operation does not add elements
Novice C + + programmers may think that vector subscript operations can add elements, but not:
Vector<int> Ivec; Empty vector for
(vector<int>::size_type IX = 0; IX = ten; ++ix)
Ivec[ix] = IX;//Disaster:ivec has no Elements
The above program attempts to insert 10 new elements into the Ivec, with the element values sequentially from 0 to 9 integers. However, here Ivec is an empty vector object, and the subscript can only be used to get an existing element.
The correct notation for this loop should be:
for (Vector<int>::size_type IX = 0; IX! = ++ix)
ivec.push_back (ix);//Ok:adds new element with value IX
Must be an existing element to be indexed with the subscript operator. When you assign a value by using the subscript operation, no element is added.
warning : subscript can only be done for elements that are known to exist
It is important to use the subscript operator ([] operator) to extract only elements that do exist, such as:
Vector<int> Ivec; Empty vector
cout << ivec[0];//Error:ivec has no elements!
Vector<int> IVEC2 (10); Vector with ten elements
cout << ivec[10];//Error:ivec has elements 0...9
Attempting to get an element that does not exist inevitably produces a run-time error. As with most similar errors, there is no guarantee that the execution process can catch such errors, and the result of running the program is indeterminate. Because the result of a non-existent element is undefined, different implementations result in different results, but the program will almost certainly fail in some interesting way when it runs.
This warning applies to any use of subscript operations, such as the subscript operation of type String, and the subscript operation of the built-in array that will be briefly described.
Unfortunately, trying to subscript non-existent elements is a serious mistake that is often made during programming. The so-called "buffer Overflow" error is the result of subscript operations on non-existent elements. Such defects often lead to the most common security issues in PCs and other applications.
Vector iterators
In addition to using subscripts to access the elements of a vector object, the standard library provides another way to detect elements: using iterators (iterator). An iterator is a type of data that allows a programmer to examine elements within a container and implement an element traversal.
The standard library defines an iterator type for each of the standard containers, including vectors. Iterator types provide a more generalized approach than subscript operations: All standard library containers define the appropriate iterator types, and only a handful of containers support subscript operations. Because iterators are applicable to all containers, modern C + + programs tend to use iterators rather than subscript operations to access container elements, even for vector types that support subscript operations.
Iterator of Containers type
Each container type defines its own iterator type, such as vector:
Vector<int>::iterator ITER;
This statement defines a variable named ITER whose data type is the iterator type defined by vector<int>. Each standard library container type defines a member named iterator, where the iterator has the same meaning as the actual iterator type.
Different container classes define their own iterator types, which are used to access the elements within the container. In other words, each container defines a type named iterator, which supports the various behaviors of (conceptual) iterators.
begin and end operations
Each container defines a pair of functions named Begin and end, which are used to return iterators. If there are elements in the container, the iterator returned by begin points to the first element:
Vector<int>::iterator iter = Ivec.begin ();
The above statement initializes ITER to the value returned by the vector operation named begin. Assuming that the vector is not empty, after initialization, ITER means that the element is ivec[0].
The iterator returned by the end operation points to the next "end element of the vector". is often referred to as an out-of-end iterator (Off-the-end iterator), indicating that it points to a non-existent element. If the vector is empty, begin returns an iterator that is the same as the iterator returned by end.
The iterator returned by the end operation does not point to any actual element in the vector, but instead it acts as a Sentinel (Sentinel), indicating that we have processed all the elements in the vector.
Self-increment and dereference operations of Vector iterators
The iterator type defines actions to get the element that the iterator points to, and allows the programmer to move the iterator from one element to another.
An iterator type can use the dereference operator (* operator) to access the R element that the iterator points to:
*iter = 0;
The dereference operator returns the element to which the iterator is currently pointing. Assuming that ITER points to the first element of the vector object Ivec, then *iter and ivec[0] are pointing to the same element. The effect of the above statement is to assign the value of this element to 0.
The iterator uses the increment operator to move the iterator forward to point to the next element in the container. Logically, the self-increment of an iterator is similar to the self-increment operation of an int object. For an int object, the result of the operation is to "add 1" to the int value, while the iterator object is to "move forward one position" in the container. Therefore, if ITER points to the first element, then ++iter points to the second element.
Because the iterator returned by the end operation does not point to any element, it cannot be dereferenced or self-increment.
additional operations for iterators
Another pair of actions that can be performed on iterators is comparison: compare two iterators with the = = or! = operator, and if the two iterator objects point to the same element, they are equal, otherwise they are not equal.
Examples of programs that iterators apply
Assuming that you have declared a vector<int>-type Ivec variable, to reset all its element values to 0, you can use the subscript operation to complete:
Reset all the elements in Ivec to 0 for
(vector<int>::size_type IX = 0; IX! = Ivec.size (); ++ix)
Ivec[ix] = 0;
The above program iterates through the elements of the Ivec with a For loop, and the For Loop defines an index IX, which will increment by 1 for each iteration of the loop. The For loop body assigns each element of the Ivec a value of 0.
A more typical practice is to use iterators to write loops:
Equivalent loop using iterators to reset all the elements in Ivec to 0 for
(vector<int>::iterator iter = Ivec . Begin ();
Iter! = Ivec.end (); ++iter)
*iter = 0;//set element to which ITER refers to 0
The For loop first defines ITER and initializes it to the first element that points to Ivec. The condition of the For loop tests whether ITER is not equal to the iterator returned by the end operation. Each iteration of ITER increases by 1, and the effect of this for loop is to process each element in the vector sequentially, starting with the first element of Ivec. Finally, ITER will point to the last element in the Ivec, and after the last element is processed, ITER increases by 1, which is equal to the return value of the end operation, in which case the loop terminates.
The statement in the For loop body accesses the value of the current element with the dereference operator. As with the subscript operator, the return value of the dereference operator is an lvalue, so it can be assigned to change its value. The effect of these loops is to assign all the elements in the Ivec to a value of 0.
Through the detailed analysis of the code above, it can be seen that this program with the subscript operator version achieves the same effect: starting with the first element of the vector, each element in the vector is set to 0.
If the vector is empty, the program is safe. If Ivec is empty, then begin returns an iterator that does not point to any element, because there is no element, so it cannot point to any element-in this case, the iterator returned from the begin operation is the same as the value of the iterator returned from the end operation, so the test condition in the For statement immediately fails.
const_iterator
The previous program uses Vector::iterator to change the value of the elements in the vector. Each container type also defines a type named Const_iterator that can access only the elements inside the container, but cannot change its value.
When we dereference an ordinary iterator type, we get a non-const reference to an element. And if we dereference the const_iterator type, we can get a reference to the const object, which, like any constant, cannot be overridden.
For example, if text is a vector<string> type, the programmer wants to traverse it and output each element so that it can write a program like this:
Use const_iterator because we won ' t change the elements for
(vector<string>::const_iterator iter = Text.begin ();
Iter! = Text.end (); ++iter)
cout << *iter << endl;//print each element in text
This loop is similar to the previous one except that it reads the element value from the iterator instead of assigning it a value. Since this is only necessary to read with an iterator, it does not need to be written, which defines ITER as the const_iterator type. When the const_iterator type is dereferenced, a const value is returned. Assignment with Const_iterator is not allowed:
for (Vector<string>::const_iterator iter = Text.begin ();
Iter! = Text.end (); + + iter)
*iter = ""; Error: *iter is const
When using the Const_iterator type, we can get an iterator whose own value can be changed, but cannot be used to change the value of the element it points to. You can self-increment an iterator and use the dereference operator to read the value, but you cannot assign a value to the element value.
Do not confuse the Const_iterator object with the Const iterator object. When declaring a const iterator, you must initialize the iterator. Once initialized, it is not possible to change its value:
Vector<int> Nums (10); Nums is nonconst
const vector<int>::iterator CIT = Nums.begin ();
*cit = 1; Ok:cit can change it underlying element
++cit;//Error:can ' t change the value of CIT
const VECTOR<INT> ; Nines (10, 9); Cannot change elements in Nines
//error:cit2 could change the element it refers to and Nines are const
Const V Ector<int>::iterator cit2 = Nines.begin ();
Ok:it can ' t change an element value, so it can be used with a const vector<int>
vector<int>::const_ite Rator it = Nines.begin ();
*it = 10; Error: *it is const
++it;//Ok:it Not const so we can change it value
//An iterator that cannot write El Ements
vector<int>::const_iterator
//An iterator whose value cannot change
const VECTOR<INT >::iterator
arithmetic operations of iterators
In addition to the increment operator for one element of a move iterator at a time, vector iterators (few other standard library container iterators) also support other arithmetic operations. These operations are called iterator arithmetic operations (iterator arithmetic), including:
L
ITER + N
iter-n
You can add or subtract an integer value to an iterator object. Doing so will result in a new iterator whose position precedes (plus) or after (minus) n elements of the element that ITER refers to. The result after addition or subtraction must point to an element in the vector indicated by ITER, or the latter element at the end of the vector. The type of the added or subtracted value should be the Size_type or difference_type type of the vector (see explanation below).
L Iter1-iter2
The expression is used to calculate the distance of two iterator objects, which is the value of the signed integer type named Difference_type, where the Difference_type type is similar to the Size_type type and is also defined by the vector. Difference_type is a signed type because the subtraction operation can produce negative results. The type can be guaranteed to be large enough to store the distance between any two iterator objects. Both Iter1 and Iter2 must both point to the element in the same vector, or to the next element after the end of the vector.
You can use an iterator arithmetic operation to move an iterator directly to an element, for example, the following statement is positioned directly on the middle element of the vector:
Vector<int>::iterator mid = Vi.begin () + vi.size ()/2;
The code above is used to initialize mid so that it points to the element closest to the middle in VI. This method of directly computing iterators is equivalent to the method of using iterators to get the intermediate elements by element-by-increment operations, but the former is much more efficient.
Any action that alters the length of the vector will invalidate an existing iterator. For example, after calling Push_back, you can no longer trust the value of the iterator that points to the vector.