3.3 Standard Library Vector
The standard library type vector represents a collection of objects, where all objects have the same type. Each object in the collection has an index corresponding to it, and the index is used to access the object. Because the vector "carries" other objects, it is also often called a container.
To use vector, the appropriate header file must be included. In subsequent examples, it will be assumed that the following using statement is made;
#include<vector> usingstd::vector;
The C++ language includes both class templates (classpress) and function templates, where vector is a class template. Only by having a very in-depth understanding of C++ can we write templates. In fact, we will not learn how to customize templates until Chapter 16. Fortunately, even if we don't know how to create a template, we can try to use it first.
A template itself is not a class or function. Instead, it can be regarded as a description of the compiler's generation of classes or functions. The process of a compiler creating a class or function based on a template is called instantiation. When using a template, it is necessary to point out what type the compiler should instantiate the class or function to.
For class templates, we provide some additional information to specify what kind of class the template is instantiated into, and what information needs to be provided is determined by the template. The way to provide information is always like this: follow a pair of angle brackets after the template name, and put information in the brackets.
Taking vector as an example, the additional information provided is the type of the object stored in vector:
vector<int> ivec;//ivec saves an int type object vector<Sales_item> Sales_vec//A object of type Salesitem vector<vector<string>> file;//The elements of this vector arevectozObject
In the above example, the editor generates three different types according to the template vector, vector, vector<Sales_item> and vector<vector>. Vector is a template, not a type, and the type generated by vector must contain the type of the element in the vector, such as yector.
A vector can accommodate most types of objects as its elements, but because a reference is not an object, there is no vector containing a reference. In addition, most other (non-referenced built-in types and class types can form vector objects, and even the elements that make up vectors can also be vectors.
It should be pointed out that in the earlier versions of the C++ standard, if the vector element was still a vector (or other template type), the definition form was slightly different from the current C++11 new standard. In the past, a space had to be added between the right angle bracket of the outer vector object and its element type, if it should be written as vector<vector> instead of vector<vector>
WARNNING: Some compilers may still need to use old-fashioned declaration statements to process vector objects with elements as vectors, such as vector<vector>.
Define and initialize vector object
Like any class type, the vector template controls the methods of defining and initializing vectors. Table 3.4 lists common methods for defining vector objects.
Table 3.4: Methods to initialize vector object
vectorv1 | v1 is an empty vector whose potential element is of type, performs default initialization |
vectorv2(v1) | v2 contains copies of all elements of v1 |
vectorv2 =v1 | Equivalent to v2(v1), v2 contains copies of all elements of v1 |
vectorv3(n,val) | v3 contains n repeated elements, and the value of each element is val |
vectorv4(n) | v4 contains n objects that perform value initialization repeatedly |
vectorv5{ta,b,c…) | v5 contains elements with the number of initial values, each element is assigned the corresponding initial value |
vectorv5=ta,b,c…) | Equivalent to v5tarbyc… |
The vector object can be initialized by default, thereby creating an empty vector of the specified type:
vector<string>syec; //Default initialization,svecNo elements included
It seems that empty vectors don't work, but we will soon know that the program can efficiently add elements to the vector object when it is run. In fact, the most common way is to define an empty vector first, and then add the value of the element one by one when it is run.
Of course, you can also specify the initial value of the element when defining the vector object. For example, it is allowed to copy elements of one vector object to another vector object. At this time, the element of the new vector object is a copy of the corresponding element of the original vector object. Note that the types of the two vector objects must be the same:
vector<int> ivec;//The initial state is empty//Add some values to ivec herevector<int> ivec2(ivec);//Copy the tvec element to ivec2vector<int> tvec3=ivecy;//Copy the ivec element to ivec3vector<string> svec(tvec2);//mistake:svecThe element isstringObject,notnt
List initialization vector object
The new C++11 standard also provides another method of assigning initial values to elements of vector objects, namely list initialization. At this time, 0 or more initial element values loaded with curly braces are assigned to the vector object:
vector<string> articles = {“a“,“an“,“the“};
The above vector object contains three elements: the first is the string "a", the second is the string "an", and the last is the string "the".
As mentioned before, C++ language provides several different initialization methods. In most cases, these initialization methods can be used equally to each other, but this is not always the case. Two exceptions that have been introduced at present are: First, when using copy initialization (that is, when using =), only one initial value can be provided; second, if the initial value is provided in the class, only copy initialization or curly braces can be used. The third special requirement is that if a list of initial element values is provided, the initial values can only be placed in curly braces for list initialization, and not in parentheses:
vector<string>V1{“a“,“anm,“the“};// List initializationvector<string>v2(“a“,“an“,“the“);//mistake
Create a specified number of elements
You can also initialize the vector object with the number of elements contained in the vector object and the unified initial value of all elements:
vector<int>ivec(10,-1)}//10 int type elements, each of which is initialized to -1vector<string>svec(10,“hi!“);//10 elements of type string, //Each is initialized to“hi1
Value Initialization
Normally, you can only provide the number of elements contained in the vector object without omitting the initial value. At this time, the library will create a value-initialized element initial value and assign it to all elements in the container. This initial value is determined by the type of element in the vector object.
If the element of the vector object is of a built-in type, such as int, the initial value of the element is automatically set to 0. If the element is a mast type, such as string, the element is initialized by the class by default:
vector<int>ivec(10); //10 elements, each initialized to 0vector<string>svec(10); //10Elements,All ginseng is emptystringObject
There are two special restrictions on this initialization method: First, some classes require that the initial value must be clearly provided. If the type of element in the vector object does not support default initialization, we must provide the initial element value. For objects of this type, only providing the number of elements without setting the initial value cannot complete the initialization work.
Second, if only the number of elements is provided without setting the initial value, you can only use direct initialization:
vector<int>vi=10;//mistake:The vector size must be specified using the form of direct initialization
Here 10 is used to illustrate how to initialize the vector object. The original intention of using it is to create a vector object with elements initialized with 10 values, rather than copying the number 10 into the vector. Therefore, copy initialization is not necessarily used at this time.
List initial value or number of elements?
In some cases, the true meaning of initialization depends on whether curly or round brackets are used when passing the initial value. For example, when initializing a vector with an integer, the meaning of an integer may be the capacity of the vector object or the value of the element. Similarly, when using two integers to initialize a vector, these two integers may be the capacity of the vector object and the initial value of the element. It may be that they are the initial value of two elements in a vector object with a capacity of 2. These meanings can be distinguished by using curly braces or round brackets:
vector<int>v1(10);// vl has 10 elements, each of which is 0vector<int>v2{10};// v2 has 1 element, the value of this element is 10vector<int>v3(10,1);// v3 has 10 elements, each of which is 1vector<int>v4{10,1};//v4have2Elements,The values are10and1
If you use parentheses, you can say that the provided value is constructed by Cconstruct)vectoz object using indica. For example, the initial value of v1 indicates the capacity of the vector object; the two initial values of v3 indicate the capacity of the vector object and the initial value of the element respectively.
If curly braces are used, it can be expressed as we want to list initialize the vector object. That is to say, the initialization process will treat the values in curly braces as a list of elements' initial values as much as possible. Other initialization methods will be considered only when list initialization cannot be performed. In the above example, the initial provided to v2 and v4
All values can be used as the values of elements, so they all perform list initialization, vector object v2 contains one element and vector object v4 contains two elements.
On the other hand, if the braces are used during initialization but the provided value cannot be used for list initialization, you should consider using such values to construct the vector object. For example, to list initialization of a vector object containing a string object, you should provide an initial value that can be assigned to the string object. At this time, it is not difficult to distinguish whether to list the elements of the vector object or to construct the vector object with a given capacity value:
vector<string>v5{“hi“}; // List initialization: v5 has an elementvector<string>v6(“hi“); //Error: Cannot build vector object using string literalsvector<string>v7{10}; //v7 has 10 elements initialized by defaultvector<string>V8{10,"hin1"}; //v8have10The value isvhi“Elements
Although curly braces are used in the above example except for the second statement, in fact, only v5 is the list initialization. To initialize the vector object on the list, the value in the braces must be the same as the element type. Obviously, the string object cannot be initialized with int, so the values provided by v7 and v8 cannot be used as the initial value of the element. After confirming that list initialization cannot be performed, the compiler will try to initialize the vector object with the default value.
Add elements to vector object
For vector objects, the direct initialization method is suitable for three situations: the initial value is known and the number is small, the initial value is a copy of another vector object, and the initial value of all elements is the same. However, it is more common: when creating a vectozr object, the actual number of elements required is not clear, and the value of the element is often impossible to determine. Sometimes, even if the initial value of the element is known, if the total amount of these values is large and different, it will be too cumbersome to perform the initialization operation when creating the vector object.
For example, if you want to create a vector object that contains 10 elements from 0 to 9, it is easy to do this using the list initialization method; but what if the vector object contains elements from 0 to 99 or from 0 to 999? At this time, it is not appropriate to list all the elements one by one through list initialization. For this example, a better way to do this is to create an empty vector first, and then use the vector's member function push_back to add elements to it at runtime. push_back is responsible for pressing a value as the tail element of the vector object to the "back" of the vector object. For example:
vector<int>v2;//Empty vector objectfor(int i=0;i!=100;++i) v2.Push_back();//Put the integer value to the end of v2 in turn //After the cycle endsv2have100Elements,Value from0arrive99
In the above example, although you know that the vector object will contain 100 elements at the end, it is declared as an empty vector at the beginning, and the next integer is added to it in sequence as a new element of v2 at each iteration.
Similarly, if you can't know the exact number of elements in the vector object until runtime, you should also use this method just now to create the vector object and assign it a value. For example, sometimes you need to read the data in real time and then assign it to a vector object:
//Read the word from standard input and store it as an element of the vector objectstring word; vector<string> text;//Empty vector objectwhile(cin>>word) { text.push_back(word);//Add word to the back of text}
Like the previous example, this example also creates a vector first, then reads in unknown numbers and saves them to text.
Key concept: vector objects can grow efficiently
The C++ standard requires that vectors should be able to add elements efficiently and quickly at runtime. Therefore, since the vector object can grow efficiently, it is not necessary to set its size when defining the vector object. In fact, if this is done, the performance may be worse. There is only one exception, that is, the values of all (all) elements are the same. Once the value of the element is different, a more effective way is to define an empty vector object first, and then close it at runtime to add specific values. In addition, vector also provides methods that allow us to further improve the performance of dynamically adding elements.
At the beginning, create an empty vector object and dynamically add elements at runtime. This practice is different from the usage of built-in array types in C and most other languages. Especially if you are used to using C or Java, it is expected that it is best to specify the capacity when creating a vector object. However, in fact, the usual situation is
Programming assumptions for adding elemental implicit to vector objects
Many programming work has been greatly simplified because it can efficiently and conveniently add elements to the vector object. However, this simplicity is also accompanied by some higher requirements for writing programs: one of them is to ensure that the written loop is correct, especially when the loop may change the capacity of the vector object.
With more use of vectors, we will gradually understand some other implicit requirements, one of which is what we should point out now: if the loop body contains statements that add elements to the vector object, the scope for loop cannot be used.
WARNING: The size of the sequence created by the range for statement should not be changed in the body.
Other vector operations
In addition to push_back, vector also provides several other operations, most of which are similar to string-related operations. Table 3.5 lists some of them.
Table 3.5: Operations supported by vector
() | If v does not contain any elements, return true; otherwise, return false |
() | Returns the number of elements in v |
Y.Push_back(t) | Add an element with a value of t to the end of v |
v[n] | Returns the reference to the element at the nth position in v |
v1=v2 | Replace elements in v1 with copies of elements in v2 |
vl={a,b,c…} | Replace elements in vl with copies of elements in list |
vl==v2 | vl and v2 are equal if and only if their number of elements is the same east and the corresponding position of the same element value are the same |
<,<=,>,>= | As the name implies, comparisons in dictionary order |
The method of accessing elements in a vector object is similar to the method of accessing characters in a string object, and it is also through the position of elements in the vector object. For example, you can use a scope for statement to process all elements in a vector object:
vector<int>V{112,3,4,5,6,718,917} for(auto&:v)//For each element in it (note: i is a reference) i*=i;// Find the square of the element valuefor(auto i:v)//For each element incout<<i<<" ";//Output this elementcout<<endl;
The first loop defines the control variable i as a reference type, so that the element of v can be assigned through i, where the type of i is specified by the auto keyword. A new compound assignment operator is used here. As we know, += adds the left operation object and the right operation object, and the result is stored in the left operation object; similarly, *= multiplies the left operation object and the right operation object, and the result is stored in the left operation object. Finally, the second loop outputs all elements.
The empty and size members of vector are exactly the same as the same name as the string: empty checks whether the vector object contains elements and returns a boolean value; size returns the number of elements in the vector object, and the return value type is the size_type type defined by vector.
The individual equality operators and relational operators are also consistent with the corresponding operator functions of string. Two vector objects are equal if and only if the number of elements they contain is the same, and the element values at the corresponding positions are the same. Relational operators are compared in dictionary order: if the capacity of the two vector objects is different, but the element values at the same position are the same, the vector objects with fewer elements are smaller than the vector objects with more elements; if the values of the elements are different, the size relationship of the vector objects is determined by the size relationship of the first pair of different element values.
The vector object can only be compared when the value of the element is comparable. Some classes, such as string, do define their own equality operators and relational operators.
Calculate the index of objects in vector
Use the subscript operator to get the specified element. Like string, the subscript of the vector object also starts from 0, and the subscript type is the corresponding size_type type. As long as the vector object is not a constant, it can assign values to the elements returned by the subscript operator. In addition, the index of the object in the vector can be obtained through calculation, and then the elements at the index position can be directly obtained.
For example, suppose there is a set of scores, where the scores are valued from 0 to 100. With 10 points as a score segment, it is required to count how many scores each score segment has. Obviously, there are 101 possible scores from 0 to 100, and these scores are distributed in 11 scores: every 10 scores constitute a score segment, there are 10 such score segments, and an additional score segment indicates a full score of 100 points. In this way, the first score will count the number of scores between 0 and 9; the second score will count the number of scores between 10 and 19, and so on. The last score segment counts the number of full scores of 100 points.
According to the above description, if the entered score is as follows:
42 65 95 100 39 67 95 76 88 76 83 92 76 93
The output result should be:
0 0 0 1 1 0 2 3 2 4 1
The results show that there are no scores below 30 points, 1 score from 30 points to 39 points, 1 score from 40 points to 49 points, 50 points, 59 points, 2 score from 60 points to 69 points, 3 score from 70 points to 79 points, 2 score from 80 points to 89 points, 4 score from 90 points to 99 points, and 1 score is full.
In the specific implementation, a vector object containing 11 elements is used, and each element is used to count the number of scores that appear on each fraction segment. For a certain score, dividing it by 10 can get the corresponding score segment index. Note: When two integers are divided, the result is still an integer, and the remainder part is automatically ignored. For example, 42/10=4, 65/10-6, 100/10=10, etc. Once the fraction segment index is calculated, it can be used as the subscript of the vector object, and then the count value of the fraction segment is obtained and 1 is added:
//The number of scores is counted as a score segment: 0~9,10~19,...,90~99,100vector<unsigned> scores(11,0); //The 11 fraction segments are all initialized to 0unsigned grade; while(cin>>grade)《//Read the results if(grzade<=100)//Only handle valid results ++scores[grade/10];//Add the count value of the corresponding fraction segment1
In the above program, first define a vector object to store the number of scores on each fraction segment. In this example, since the value of each element is the same in the initial state, we applied for 11 elements for the vector object and set the initial values of all elements to 0. The conditional part of the while statement is responsible for reading the score. In the loop body, first check whether the read score is legal (that is, whether it is less than or equal to 100 points). If it is legal, add 1 to the count value of the score corresponding to the score.
The statement that performs the accumulation of count values well reflects the simplicity of C++ program code. expression
++scores[grade/10];//Add the count value of the current fraction segment by 1
Equivalent to
auto ind = grade/10;//Get the fractional segment indexscores[tnd]=scores[ind]+1;//Add count value1
The meaning of the above statement is: divide grade by 10 to calculate the score segment where the score is located, and then use the result as the subscript of the variable scores. By running the subscript operation, the count value corresponding to the fraction segment is obtained. Since a new score belonging to the fraction segment appears, the count value is increased by 1.
As mentioned earlier, when using subscripts, you must clearly know whether they are within a reasonable range. In this program, we confirm in advance that the input score is indeed between 0 and 100, so the calculated subscript must be between 0 and 10, which belongs to the valid range specified by 0 to ()-1 and must be legal.
You cannot add elements in subscript form
Programmers who are new to C++ may think that elements can be added through the subscript form of vector object, but this is not the case. The following code tries to add 10 elements to the vector object ivec:
vector<int>tvec;//Empty vector objectfor(decltype(())ix = 0;ix!=10;++ix) ivec[ix]=ix;//Critical error:tvecNo elements included
However, this code is wrong: ivec is an empty vector that does not contain any elements at all, so of course it cannot access any elements through subscripts! As mentioned earlier, the correct way is to use push_bacK:
for(decltype(())ix=0;ix!=10;++ix) ivec.push_back(ix); //correct:Add a new element,The value of this element isix
The subscript operation of the vestor cashing (and string object) can be used to access existing elements. It cannot be used to add elements.
Tip: Only subscript operations can be performed on elements that are sure to exist!
One thing that must be clear about the history of subscripts is that only subscript operations can be performed on elements that are sure to exist. For example,
vector<int>ivec;//Empty vector objectcout<<ivec[013//Error: ivec does not contain any elementsvector<int>ivec2(10j);//Yector wealth image containing 10 elementscout<<ivec2[10];//mistake:ivec2The legal index of the element is from0arrive9
Trying to access an non-existent element in the form of a subscript will cause a mistake, but this error will not be discovered by the compiler, but will produce an unpredictable value at runtime.
Unfortunately, this behavior of accessing non-existent elements through subscripts is very common and has serious consequences. The so-called buffer overflow refers to this type of error, which also leads to PC and other
An important reason for security issues with applications on the device.
This is the end of this article about the detailed explanation of the C++ Primer standard library vector example. For more related C++ Primer standard library vector content, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!