C++STL——vector class

Table of contents

1. Preliminary introduction to the vector class:

2. Introduction to the member functions of the vector class:

        2.1 Basic member function usage:

                2.1.1reserve(): expansion function 

                Code quiz:

                2.1.2resize() adjusts the data storage function of class objects

                Code experiment: 

                 result:

        ​Edit         

                2.1.3 Shrinking function: shrink_to_fit

        2.2 Iterator

        Code experiment:

        operation result: 

         2.3 Operator overloading, at(), back(), front() functions

        Code experiment:

        The at(); function has the same function as operator[], which can obtain an element in the class object array.

        Code experiment:

         operation result:

        For the front() and back() functions, they obtain the head and tail elements in the class object array.

        2.4assign() function:

        Usage 2: 

        Usage 1: Use iterators as formal parameters:

operation result: 

        2.5 Add data

        operation result:

        2.6 Delete elements 

        Code experiment:

        operation result:

        2.7 Search 

        Code experiment:

        operation result: 

        


       I talked about the underlying understanding, member functions and simulation implementation of the String character sequence class before. The String class can only be used to store characters or strings. The bottom layer is implemented by using an array that creates a heap space; what I want to talk about today is A very valuable class in STL - vector, vector in English is vector:

1. Preliminary introduction to the vector class:

        1. Vector is a sequence container that represents a variable-size array.

        2. Just like arrays, vectors also use continuous storage space to store elements. This means that you can use subscripts to access the elements of the vector, which is as efficient as an array. But unlike an array, its size can be changed dynamically, and its size will be automatically handled by the container.

        3. Essentially, vector uses a dynamically allocated array to store its elements. When new elements are inserted, the array needs to be resized to increase storage space. The approach is to allocate a new array and then move all elements to this array. In terms of time, this is a relatively expensive task, because the vector is not resized every time a new element is added to the container.

        4. Vector allocation space strategy: vector will allocate some additional space to accommodate possible growth because the storage space is larger than the actual required storage space. Different libraries use different strategies to trade off space usage and reallocation. But in any case, the reallocation should be logarithmically increasing in interval size, so that inserting an element at the end is done in constant time.

        5. Therefore, vector takes up more storage space in order to gain the ability to manage storage space and grow dynamically in an efficient manner.

        6. Compared with other dynamic sequence containers (deque, list and ), vector is more efficient when accessing elements, and adding and deleting elements at the end is relatively efficient. For other deletion and insertion operations that are not at the end, the efficiency is even lower. Better than unified iterators and references in lists.

2. Introduction to the member functions of the vector class:

        2.1 Basic member function usage:

#include<iostream>
#include<vector>
using namespace std;

void Test1() {
	vector<int> v;
	cout << v.size() << endl;
	cout << v.capacity() << endl;
	cout << v.empty() << endl;		//empty为空则显示1,不为空则显示0
    cout<<
	v.push_back(10);
	v.push_back(185.95);	//舍弃小数,取整,不存在过.5进一
	v.push_back('c');	//v是int类型,所以转换的是ASCII码值
	v.push_back(7999);
	v.push_back(0);
	cout << v.size() << endl;
	cout << v.capacity() << endl;
	cout << v.empty() << endl;
    cout << "遍历所得:";
	for (auto& ch : v) {
		cout << ch << " ";
	}
	cout << endl;
	cout << v.max_size() << endl;		
//获取的是可以存储的总个数,10亿多,最大值是42亿多字节,因为是int类型,占4个字节,所以总字节数/int类型
	cout << "----------------------" << endl;
}

int main(){
    Test1();
    return 0;
}

 result:

        2.1.1reserve(): expansion function 

        The formal parameter of reserve has only one size type value n, which can change the capacity of the class object. It will only increase the capacity (expand it), but will not reduce it.

       1. If n is greater than the current vector capacity, this function causes the container to reallocate its storage, increasing its capacity to  n (or greater).
        2. In all other cases, function calls do not cause reallocation and capacity is not affected.

        This function has no effect on the size of the class object and cannot change its elements.

Code quiz: 

        Still based on the example of the object v created above:

cout << "使用reserve前: " << endl;

	cout << "v.size(): " << v.size() << endl;
	cout << "v.capacity(): " << v.capacity() << endl;
	cout << "使用reserve(5)后: " << endl;	
	v.reserve(5);					//——只改变容量
	cout << "v.size(): "<<v.size() << endl;
	cout << "v.capacity(): "<<v.capacity() << endl;
	cout << "使用reserve(20)后: " << endl;
	v.reserve(20);					//——只改变容量
	cout << "v.size(): " << v.size() << endl;
	cout << "v.capacity(): " << v.capacity() << endl;
	cout << "使用reserve(2)后: " << endl;
	v.reserve(2);					//——只改变容量
	cout << "v.size(): " << v.size() << endl;
	cout << "v.capacity(): " << v.capacity() << endl;
	cout << "----------" << endl;

        In the vector class, whenever the amount of data storage reaches the capacity critical value, adding more data will trigger the expansion mechanism. Under normal circumstances, the expansion is expanded to about 1.5 times to 2 times the original size. This is what computer scientists have experienced countless times. According to the results of these tests, expansion of 1.5 times to 2 times is very appropriate. It can avoid the problem of frequent expansion due to under expansion, and avoid the problem of wasted space caused by over expansion!

The above is the expansion mechanism in the VS compiler, which is performed by about 1.5 times. 


    

        2.1.2resize() adjusts the data storage function of class objects

Function: Adjust the size of the container so that it contains  n elements.
        1. If n is less than the current container size, the content is reduced to its first  n  elements, removing the excess elements (and destroying them).
        2. If n is larger than the current container size, extend the contents by inserting the required number of elements at the end to reach  the size of n  . If val is specified  , new elements will be initialized to  a copy of val  , otherwise, they will be value-initialized.
        3. If  n  is also larger than the current container capacity, the allocated storage space will be automatically reallocated. 

        value_type() is the default value of the second parameter, which represents the class template generic T and will call the constructor of the T template. When I first learned about this function, I thought why not set it to 0?

        After a long period of study, I found an important reason: for built-in types char/int/double/size_t, etc., they can be set to 0, but what if the resize of a custom class is modified? Can it also be set to 0? Custom classes are unknown and uncertain, so it is better to use anonymous objects. For resize of built-in types, anonymous objects can also call the constructors of built-in types - the default is 0

​ 

Code experiment: 
cout << "resize: " << endl;
	cout << "遍历所得:";
	for (auto& ch : v) {
		cout << ch << " ";
	}
	cout << endl;
	cout << "使用resize前: " << endl;
	cout << v.size() << endl;
	cout << v.capacity() << endl;
	//使用resize,让v的顺序表只留下3个值
	v.resize(3);
	cout << "v.resize(3):缩减_size,容量不会变" << endl;
	cout << v.size() << endl;
	cout << v.capacity() << endl;
	cout << "遍历所得:";
	for (auto& ch : v) {
		cout << ch << " ";
	}
	cout << endl;
	cout << "----------" << endl;

	cout << "resize(6)表示扩容,且剩余的几个数按a的ASCII码值填写:" << endl;
	v.resize(6, 'a');
	cout << "遍历所得:";
	for (auto& ch : v) {
		cout << ch << " ";
	}
	cout << endl;
	cout << v.size() << endl;
	cout << v.capacity() << endl;
	cout << "----------" << endl;

	//resize代表扩容15个数值,_size=15,‘
	v.resize(15);
	for (auto& ch : v) {
		cout << ch << " ";
	}
	cout << endl;
	cout << v.size() << endl;
	cout << v.capacity() << endl;
 result:

 resize(3); The data of the class object is 5 as shown above. Setting the actual parameter to 3 will reduce the amount of data stored in the class object, but will not affect the capacity;

​ 

        resize(6,'a'); The data amount of the class object is from 3-->6. The three extra data will be determined according to the second parameter written. The second parameter is the character 'a', which is converted into an integer Promotion from char-->int, the ASCII code value of 'a' is 97

       resize(15); Since the second parameter is not set, the function defaults to the default value: value_type(), which defaults to 0.


        2.1.3 Shrinking function: shrink_to_fit

        Under normal circumstances, no one will use the shrink_to_fit() function, unless you expand the class object very much and do not use so much capacity. You can use this function to reduce the capacity.

        The principle of shrinking and expanding is the same. They both recreate a new heap space, copy the data to the new space, and then release the original old space. Reduction and expansion both trade time for space and come at a cost.

        The code will not be demonstrated here.


        2.2 Iterator

        Iterator is the core of every STL container. With iterator, we can easily traverse it to view the content and make modifications.

        The above are the interface functions in the iterator. We can regard them as pointers. For example: begin points to the beginning of the content of the vector class object, and end points to the end of the content of the vector class object. In iterators, there are three types of begin and end, namely forward iteration, reverse iteration, and const reverse iteration. These three types of interfaces are restricted by the vector class domain, so you need to add the class domain when defining these begin and end!

   To use iterators, you need to understand its format:

std::vector:: iterator iterator object name (choose your own name) = vector class object.begin(); 

Code experiment:
#include<iostream>
#include<vector>
using namespace std;

void Test1() {
	vector <int> v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);

	//迭代器
	cout << "正向迭代器的遍历:";
	vector<int>::iterator vit = v.begin();
	while (vit != v.end()) {
		cout << *vit << " ";
		++vit;
	}
	cout << endl;


	cout << "反向迭代器的遍历:";
	vector<int>::reverse_iterator rvit = v.rbegin();
	while (rvit != v.rend()) {
		cout << ++(*rvit) << " ";
		++rvit;
	}
	cout << endl;


	cout << "const正向迭代器的遍历:";
	vector<int>::const_iterator cvit = v.cbegin();
	while (cvit != v.cend()) {
		//cout << --(*cvit) << " ";	报错
		cout << *(cvit) << " ";
		++cvit;
	}
	cout << endl;

	cout << "const反向迭代器的遍历:";
	vector<int>::const_reverse_iterator crvit = v.crbegin();
	while (crvit != v.crend()) {
		//cout << --(*crvit) << " ";	报错
		cout << *(crvit) << " ";
		++crvit;
	}
	cout << endl;

}

int main(){
    Test1();
    return 0;
}
operation result: 

        It can be seen from the results that: iterators containing const cannot modify the data of class objects. Compared with forward iterators, the reverse iterator's begin/end and forward begin/end are two pointers with opposite positions.


         2.3 Operator overloading, at(), back(), front() functions

Code experiment:
void Test2() {
	vector <int> v;
	for (int i = 0; i < 10; ++i) {
		v.push_back(i * 10);
	}
	
	//operator[]访问
	cout << "operator[]遍历:";
	for (int i = 0; i < v.size(); ++i) {
		//读操作
		cout << v[i] << " ";
	}
	cout << endl;

}

[ ], these square brackets can make it easier for us to traverse it, because the bottom layer of vector is an array, so it is very convenient to use [ ] overloading!

The at(); function has the same function as operator[], which can obtain an element in the class object array.

Code experiment:
//at与operator[]几乎一样的作用
	cout << "at函数遍历:";
	for (int i = 0; i < v.size(); ++i) {
		//读操作
		cout << v.at(i) << " ";
	}
	cout << endl;

	//但at函数操作不当越界时会报异常
	vector<double> v2;

	for (int i = 0; i < 10; ++i) {
		cout << v2.at(i) << " ";
	}
	cout << endl;
	cout << "------------------------------------" << endl;
 operation result:

The difference between at and operator[] is:

        However, when the at function is improperly operated and crosses the boundary, an exception will be reported; - the exception will cause the system to crash and hang up.

        Operator[] will report an assertion fail error if it crosses the boundary; the error will cause the current program to be forced to terminate.

For the front() and back() functions, they obtain the head and tail elements in the class object array.
#include<vector>
#include<iostream>
using namespce std;

void Test3(){
vector<int> v3;
	vector <int> v;
	for (int i = 0; i < 10; ++i) {
		v.push_back(i * 10);
		cout << v[i] << " ";
	}
	cout << endl;

	cout << v.front() << endl;
	cout << v.back() << endl;
}

int main(){
    Test3();
    return 0;
}


        2.4assign() function:

Assign function: allocate new content, replace its current content, and modify its size accordingly.

There are two ways to use assign, one is to use an iterator; the other is to enter n and val

        In the second method: assgin will delete all the original values ​​​​of the object v, leaving only n (first parameter) elements, and reassign them to val (second parameter)

Usage 2: 
    int main(){
	vector<int> v3;
    int i=0;
    while(i<10){
        v3.push_back(i);
    }	
    //此时v3的size变为10

    //下面代码表示:将v3的_size变为5,且全都重新赋值为3,即 3 3 3 3 3 
	v3.assign(5,3);	
	for (auto& r : v3) {
		cout << r << " ";
	}
	cout << endl;
	cout << "-------------------------" << endl;
    return 0;
    }

Usage 1: Use iterators as formal parameters:
    string s1 = "hello world";
	v3.assign(s1.begin(),s1.end());		
    //该代码表示:v3[0]='h'的ASCII码值,v3[1]='e'的ASCII码值,v3[2]='l'的ASCII码值......

	for (auto& r : v3) {
		cout << r << " ";
	}	//此时v3共有11个元素,每个元素值分别为s1各个字符的ASCII码值
	cout << endl;
	cout << v3.size() << endl;
	cout << "-------------------------" << endl;


	v3.assign(++s1.begin(), --s1.end());
    //该代码表示:去掉了string s1的头元素和尾元素后将各个字符的ASCII码值赋值给v3
	for (auto& r : v3) {
		cout << r << " ";
	}	//此时v3共有9个元素,每个元素值分别为s1除去头尾后各个字符的ASCII码值
	cout << endl;
	cout << v3.size() << endl;
operation result: 


        2.5 Add data

  Method: push_back(), insert()

Note: push_back is a tail interpolation function, so you only need to enter the value you want to add;

     For inert, you can insert at the beginning, in the middle, or at the end, but the insertion method is limited to the iterator method, and the insertion at the subscript pos position is not specified. 

void Test5() {
	vector<int> v5;
	v5.push_back(9);
	v5.push_back(8);
	v5.push_back(7);

	v5.insert(v5.begin(), 100);//表示在v5容器的首元素位置插入值为100的元素——简称头插
	for (auto& ch : v5) {
		cout << ch << " ";
	}
	cout << endl;

	v5.insert(v5.end(), 666);	//表示在v5容器的末尾元素插入值为666的元素——简称尾插

	for (auto& ch : v5) {
		cout << ch << " ";
	}
	cout << endl;

	//若想在中间某个位置插入则,
	v5.insert(v5.begin()+2, 145);	//在vector数组的第3个位置插入
	v5.insert(v5.end() -3, 467);	//在vector数组的倒数第四个位置插入
	for (auto& ch : v5) {
		cout << ch << " ";
	}
	cout << endl;
}

int main(){
    Test5();
    return 0;
}
operation result:


        2.6 Delete elements 

There are also two methods: pop_back(), erase()

 Note: pop_back is the tail deletion function;

        For erase, head deletion, middle deletion, and tail deletion are possible, and the deletion method is limited to using iterators, and deletion at the subscript pos position is not specified. 

 Code experiment:
void Test7() {
	vector<int> v7;
	v7.push_back(100);
	v7.push_back(200);
	v7.push_back(300);
	v7.push_back(400);
	v7.push_back(500);
	v7.push_back(600);
	cout << "删除前:";
	for (auto& ch : v7) {
		cout << ch << " ";
	}
	cout << endl;

	//删除

	//方法1:pop删
	cout << "pop删:";
	v7.pop_back();	//尾删
	//遍历
	for (auto& ch : v7) {
		cout << ch << " ";
	}
	cout << endl;

	//方法2:erase指定某一位置删除
	cout << "erase头删:";
	v7.erase(v7.begin());			//v7.begin()头删
	for (auto& ch : v7) {
		cout << ch << " ";
	}
	cout << endl;

	v7.erase(v7.end()-1);
	cout << "erase尾删:";
	for (auto& ch : v7) {
		cout << ch << " ";
	}
	cout << endl;

	cout << "erase中间删:";
	v7.erase(v7.begin()+1);			//v7.begin()头删
	for (auto& ch : v7) {
		cout << ch << " ";
	}
	cout << endl;
}

int main(){
    Test7();
    return 0;
}
operation result:


        2.7 Search 

In fact, the search function find() is not a member function of vector. It uses the find function in the std library.

 

       For find in the library, its formal parameter also specifies the element search according to the iterator pointer.

Code experiment:
void Test6() {
	vector<int> v6;
	v6.push_back(100);
	v6.push_back(200);
	v6.push_back(300);
	v6.push_back(400);
	v6.push_back(500);
	v6.push_back(600);


	//find不是成员函数,所以find的参数是以迭代器为准,300是vector数组的元素值,不可填下标位置
	vector<int> ::iterator it1 = find(v6.begin(),v6.end(),300);
	if(it1 != v6.end()) {
		v6.insert(it1, 90);	//在it1位置处插入数值90
	}

	//遍历
	for (auto& ch : v6) {
		cout << ch << " ";
	}
	cout << endl;
}
operation result: 

Guess you like

Origin blog.csdn.net/weixin_69283129/article/details/131899708