Preface
After the previous study of string, we have mastered many string class functions. Many class functions in vector are similar to those in string. For example, the use of iterators is the same in all containers. Here we no longer Introduction, let's learn some commonly used functions of the vector class.
1. Document introduction of vector
2. Vector represents the sequence container of variable-sized array in C++, and the header file <vector> needs to be included when using it. Just like arrays, vector also uses continuous storage space to store elements.
The use of vector
1. The constructor of vector
There are four main constructors of vector, let's demonstrate them one by one
The first is the default construction with no parameters, the second is that we can use n vals to initialize the vector, the third is the copy construction of the vector, and the fourth is the initial construction using an iterator.
int main()
{
vector<int> v1;
v1.push_back(1); //push_back的作用就是插入一个元素
v1.push_back(2);
v1.push_back(3);
v1.push_back(4);
vector<int> v2(4); //用4个匿名对象进行初始化
vector<int> v3(4, 9); //用4个9进行初始化
vector<int> v4(++v1.begin(), --v1.end());//用迭代器进行进行初始化
vector<int> v5(v3); //拷贝构造
return 0;
}
Observe and monitor:
Two, capacity-related functions
Let's look at the first three first:
int main()
{
vector<int> v1(10, 2);
cout << "size:" << v1.size() << endl;
cout << "capacity:" << v1.capacity() << endl;
cout << "empty:" << v1.empty() << endl;
return 0;
}
Then let's look at the function first reserve
. The function of the reserve function is to open up a new space to change capacity
, but it will not change size
.
- When
reserve
in the functionn
is less than the original capacity, this function does nothing. - When
reserve
in the function isn
larger than the original capacity, the capacity will be expanded, and the original data inside will not be changed.
Let's look at the following piece of code:
int main()
{
vector<int> v1(10, 2);
v1.reserve(5);
v1.reserve(15);
return 0;
}
Observing the monitoring window, we will find that the function does nothing when executing reserve(5), and the function will expand when executing reserve(15).
reserve
It is only responsible for opening up space. If you know how much space you need to use, reserve
you can alleviate the cost defect of vector capacity expansion.
Now let's look at resize
the function again, resize
the function will not only change capacity
but also change size
.
-
If
resize
the parametern
is smaller than the original onesize
, the previous one will be keptn
, and the latter data will be destroyed, butcapacity
unchanged. -
If
resise
the parametern
is larger than the original one , the space behindsize
will be initialized with the second parameter untilsize
size == capacity
-
If
resise
the parametern
is larger than the originalcapacity
, it will expand and initialize the unused space.
Let's look at the following piece of code:
int main()
{
vector<int> v1(10, 2);
v1.resize(5);
v1.resize(15,7);
return 0;
}
The discussion of vector space growth problem in
Regarding the vector space growth mechanism, we can use the following code to test.
void TestVectorExpand()
{
size_t sz;
vector<int> v;
//记录第一次的capacity的值
sz = v.capacity();
cout << "making v grow:\n";
for (int i = 0; i < 100; ++i)
{
//挨个插入100个元素
v.push_back(i);
//如果容量发生了变化,就重新更新sz的数据,并打印sz
if (sz != v.capacity())
{
sz = v.capacity();
cout << "capacity changed: " << sz << '\n';
}
}
}
Let's take a look windows
at the expansion mechanism:
We will find that windows
the platform is 1.5
doubled!
Let's take a look at Linux
the space growth under the platform:
We found that it is 2
double expansion.
The reason is that the STL versions used by the two platforms are different, vs is the PJ version STL, and g++ is the SGI version STL.
3. Access to vector data
Code demo:
int main()
{
vector<int> v1;
v1.push_back(0);
v1.push_back(1);
v1.push_back(2);
v1.push_back(3);
v1.push_back(4);
cout << v1[2] << endl;
cout << v1.at(2) << endl;
cout << v1.front() << endl;
cout << v1.back() << endl;
int* pi = v1.data();
pi[3] = 5;
cout << pi[3] << endl;
return 0;
}
4. Addition, deletion and modification of vector
1. assign function
The first function uses an iterator range for assignment, and the second function uses n
elements for assignment.
int main()
{
vector<int> v1;
v1.push_back(0);
v1.push_back(1);
v1.push_back(2);
v1.push_back(3);
v1.push_back(4);
vector<int>::iterator it1 = v1.begin();
while (it1 != v1.end())
{
cout << *it1;
++it1;
}
cout << endl;
vector<int> v2(2, 7);
vector<int>::iterator it2 = v2.begin();
while (it2 != v2.end())
{
cout << *it2 ;
++it2;
}
cout << endl;
//普通赋值
v2.assign(3, 2);
for (auto& e : v2)
{
cout << e ;
}
cout << endl;
//用迭代器区间赋值
v2.assign(v1.begin(), v1.end());
for (auto& e : v2)
{
cout << e;
}
cout << endl;
return 0;
}
2. insert function
The first function is to 1
insert an element before the pos position, and the second function is to n
insert an element before the pos position
int main()
{
vector<int> v1;
v1.push_back(0);
v1.push_back(1);
v1.push_back(2);
v1.push_back(3);
v1.push_back(4);
v1.pop_back();
for (auto& e : v1)
{
cout << e;
}
cout << endl;
v1.insert(++v1.begin(), 2, 7);
for (auto& e : v1)
{
cout << e;
}
cout << endl;
return 0;
}
3. Erase function
The first function is to delete the pos iterator position. The second is to delete all elements in the iterator range
int main()
{
vector<int> v1;
v1.push_back(0);
v1.push_back(1);
v1.push_back(2);
v1.push_back(3);
v1.push_back(4);
for (auto& e : v1)
{
cout << e;
}
cout << endl;
v1.erase(++v1.begin(), --v1.end());
for (auto& e : v1)
{
cout << e;
}
cout << endl;
return 0;
}
4. swap function
int main()
{
vector<int> v1;
v1.push_back(0);
v1.push_back(1);
v1.push_back(2);
v1.push_back(3);
v1.push_back(4);
for (auto& e : v1)
{
cout << e;
}
cout << endl;
vector<int> v2(9, 7);
v1.swap(v2);
for (auto& e : v1)
{
cout << e;
}
cout << endl;
return 0;
}
5. clear function
int main()
{
vector<int> v1;
v1.push_back(0);
v1.push_back(1);
v1.push_back(2);
v1.push_back(3);
v1.push_back(4);
for (auto& e : v1)
{
cout << e;
}
cout << endl;
v1.clear();
cout << "size:" << v1.size() << endl;
cout << "capacity :" << v1.capacity() << endl;
return 0;
}
6. find function
The find function is not a member function in vector, but a function in an algorithm library in C++. Its function is to help us find the data we want.
Function parameters : two iterators, determine one interval, and the last parameter is the element we want to find.
Return value : When the element is found, return the iterator of the position, if not found, return last
the iterator.
( Note: All iterator ranges are left-closed and right-open, so returning the last iterator means that it cannot be found )
int main()
{
vector<int> v1;
v1.push_back(0);
v1.push_back(1);
v1.push_back(2);
v1.push_back(3);
v1.push_back(4);
//删除元素 2
vector<int>::iterator it = find(v1.begin(), v1.end(), 2);
v1.erase(it);
for (auto& e : v1)
{
cout << e;
}
return 0;
}
Five, vector iterator invalidation problem
The main function of the iterator is to allow the algorithm to not care about the underlying data structure. The underlying layer is actually a pointer, or it encapsulates the pointer .
The iterator invalidation means that the space pointed to by the corresponding pointer at the bottom of the iterator is destroyed, and using a piece of space that has been released may cause undefined consequences or program crashes.
However, since the STL version vs
used g++
is the PJ version and the SGI version, the performance of the iterator invalidation is not the same in the two versions.
There are two main ways to invalidate an iterator:
- Iterator invalidation due to element deletion.
- Iterator invalidation due to capacity change
Let's first look at the following iterator invalidation caused by deleting elements:
int main()
{
vector<int> v1{
1,2,3,4,5 };
vector<int>::iterator it = v1.begin();
//删除it指向的元素
v1.erase(it);
//再对it指向的元素进行自增
(*it)++;
for (auto& e : v1)
{
cout << e ;
}
cout << endl;
return 0;
}
erase
After deleting it
the position element, it
the element after the position will move forward , but it
it is unreasonable for us to visit it again, because at first we wanted to it
point to the element 1
, the element 1
disappeared, and we should not it
visit it anymore.
The PJ version of the above code under vs will directly report an error, while the SGI version under g++ will access the next element 2
.
-
When executed under vs,
(*it)++
an error will be reported directly:
-
When executed under g++,
(*it)++
it will directly access the element moved to the position later, resulting in the2
following3
:
Seeing this, you may think that the iterator here can also be regarded as not invalid. It is reasonable to it
directly access elements under g++, but when the above code deletes the last element , it is still executed under 5
g++ (*it)++
There was a transgression!
So for this, it
if we still want to use it, we have to reassign the iterator , which is also a general method to solve the iterator invalidation.
Let's look at another iterator failure caused by capacity change:
int main()
{
vector<int> v1{
1,2,3,4 };
//it是未扩容之前的迭代器
vector<int>::iterator it = v1.begin();
v1.resize(50,0);
//v1.begin()是扩容后的迭代器
while (it != v1.end())
{
cout << *it << " ";
++it;
}
cout << endl;
return 0;
}
-
When executed under vs, an error will be reported directly:
-
An error will also be executed when executing under g++:
many out-of-bounds accesses have occurred.
The reason for the failure of the iterator here is that we v1
have expanded the iterator, it
but it points to the iterator at the starting position before the expansion. After the expansion, the memory address has changed, but it
it has not been updated in time. We also have the potential to cause a lot of out-of-bounds behavior by using previous iterators.
For the second type of iterator failure, as long as the operation that causes the underlying space to change, it may be an iterator failure, such as: resize, reserve, insert, assign, push_back, etc.
Therefore, when using functions and iterators that may cause changes in their underlying space at the same time, pay attention to the update of the iterator! !