一、vector的介绍:
1.vector是表示可变大小的顺序容器。
2.就像数组一样,vector也采用的连续存储空间来存储元素。也就是意味着可以采用下标对vector的元素 进行访问,和数组一样高效。但是又不像数组,它的大小是可以动态改变的,而且它的大小会被容器自 动处理。
3.本质讲,vector使用动态分配数组来存储它的元素。当空间满了并且插入新元素的时候,这个数组需要被重新分配大小为了增加存储空间。其做法是,分配一个新的数组,然后将全部元素移到这个数组。
4.vector分配空间策略:我们一般会按照两倍进行扩容来避免频繁扩容。
5.vector占用了更多的存储空间,为了获得管理存储空间的能力,并且以一种有效的方式动态增 长。
6.与其它动态序列容器相比(deque, list and forward_list), vector在访问元素的时候更加高效,在末 尾添加和删除元素相对高效。对于其他不在末尾的删除和插入操作,效率更低。比起list和forward_list 统一的迭代器和引用更好。
总结:它底层实际上和顺序表差不多 可以使用下标来访问 为了提高效率扩容一般是按照两倍扩容
二、vector的定义:
方法一: 构造一个某类型的空容器
vector<int> v1; // 构造一个int类型的空容器
方法二:构造一个含有n个val值的容器
vector<int> v2(10, 2); // 构造一个int类型的容器 初始化为10个2
方式三:拷贝构造
vector<int> v3(v2);
方式四:使用迭代器拷贝
vector<int> v4(v2.begin(), v2.end()); //迭代器拷贝构造
注:
1.使用别的容器的迭代器也可以拷贝(不是vector 以string为例)
string s1("hello world");
vector<char> v5(s1.begin(), s1.end()); // 使用string类的迭代器构造
2.vector也可以嵌套自己,vector<vector<int>>相当于二维数组
        第一个vector中的指针指向了一块存放vector<int>类型的空间,而之后的每一个vector中的指针又指向存放int类型的空间。(就相当于二维数组,只不过这个是动态的并且有成员函数)
三、空间相关
3.1 size
size_type就是size_t
返回容器中占用的元素个数
#include <iostream>
#include <vector>
int main()
{
	std::vector<int> myints;
	std::cout << "0. size: " << myints.size() << '\n';
	for (int i = 0; i < 10; i++) myints.push_back(i);
	std::cout << "1. size: " << myints.size() << '\n';
	myints.insert(myints.end(), 10, 100);   //从最后的位置开始插入10个100
	std::cout << "2. size: " << myints.size() << '\n';
	myints.pop_back();
	std::cout << "3. size: " << myints.size() << '\n';
	return 0;
} 
   
3.2 capacity
size_type就是size_t
#include <iostream>
#include <vector>
int main()
{
	std::vector<int> myvector;
	// set some content in the vector:
	for (int i = 1; i <= 100; i++)
	{
		myvector.push_back(i);
		if (i % 10 == 0)
		{
			std::cout << "size: " << (int)myvector.size() << '\n';
			std::cout << "capacity: " << (int)myvector.capacity() << '\n';
		}
	}
	return 0;
} 
 
VS保持一贯的作风,先有一段空间存放数据,超了之后按1.5倍扩容。
3.3 empty
![]()
判断vector是否为空(size是否为0) 为空就返回true 不为空返回false
3.4 resize
resize函数可以改变容器的有效元素个数
![]()
value_type就是模板类型T ,不传的话,就会去调用对应的 T() 构造函数
使用规则是这样子的
- 假设所给值小于当前容器size值 size缩小至所给值
 - 假设所给值大于当前容器size值并且小于capacity值 size扩大至所给值 扩大的范围赋值(调用对应的构造函数)
 - 假设所给值大于当前容器size值并且大于capacity值 先扩容 后面操作和2一样
 
调用对应的构造函数:
C++规定:内置类型也有对应的构造函数
int --> 0
double -->0.0
int* -->nullptr
对应自定义类型,调用对应的构造函数
// resizing vector
#include <iostream>
#include <vector>
int main()
{
    std::vector<int> myvector;
    // set some initial content:
    for (int i = 1; i < 10; i++) myvector.push_back(i);
    myvector.resize(5);
    myvector.resize(8, 100);
    myvector.resize(12);
    std::cout << "myvector contains:";
    for (int i = 0; i < myvector.size(); i++)
        std::cout << ' ' << myvector[i];
    std::cout << '\n';
    return 0;
} 
![]()
3.5 reserve
 ![]()
使用规则如下
1.假设所给值小于当前容量值 什么都不做
2 假设所给值大于当前容量值 扩容至所给值
// vector::reserve
#include <iostream>
#include <vector>
int main()
{
    std::vector<int>::size_type sz;//std全局域 vector<int>类 typedef的size_type  就是 size_t
    std::vector<int> foo;
    sz = foo.capacity();
    std::cout << "making foo grow:\n";
    for (int i = 0; i < 100; ++i) {
        foo.push_back(i);
        if (sz != foo.capacity()) {
            sz = foo.capacity();
            std::cout << "capacity changed: " << sz << '\n';
        }
    }
    std::vector<int> bar;
    sz = bar.capacity();
    bar.reserve(100);   // this is the only difference with foo above
    std::cout << "making bar grow:\n";
    for (int i = 0; i < 100; ++i) {
        bar.push_back(i);
        if (sz != bar.capacity()) {
            sz = bar.capacity();
            std::cout << "capacity changed: " << sz << '\n';
        }
    }
    return 0;
} 

reserve会先把空间括好,你想要括多少,就传参。
四、迭代器相关

在前面的string类里面已经讲过迭代器了。这里我就贴一下代码就过了。后面的insert和erase迭代器失效才是重头戏!
// vector::reserve
#include <iostream>
#include <vector>
using namespace std;
int main()
{
    vector<int> v1; // 构造一个int类型的容器
    for (int i = 0; i < 10; i++)
    {
        v1.push_back(i);
    }
    //正向迭代器
    vector<int>::iterator it = v1.begin();
    while (it != v1.end())
    {
        cout << *it << " ";
        it++;
    }
    cout << endl;
    //反向迭代器
    vector<int>::reverse_iterator rit = v1.rbegin();
    while (rit != v1.rend())
    {
        cout << *rit << " ";
        rit++;
    }
} 
 
五、访问数据
 
5.1 operator[] 对[]运算符进行了重载,一个通过下标来访问元素。
5.2 at和operator[] 一样,通过下标来访问。
5.3 front 访问第一个元素
5.4 back 访问最后一个元素(v.size()-1的下标的元素)
5. data 用于返回指向vector中第一个元素的指针
// vector::data
#include <iostream>
#include <vector>
int main()
{
    std::vector<int> myvector(5);
    int* p = myvector.data();
    *p = 10;
    ++p;
    *p = 20;
    p[2] = 100;
    std::cout << "myvector contains:";
    for (unsigned i = 0; i < myvector.size(); ++i)
        std::cout << ' ' << myvector[i];
    std::cout << '\n';
    return 0;
} 
 可以通过p指针来访问vector类中的元素
六、增删查改
增:
5.1 push_back 向尾部插入一个数据
vector<int> v1;
v1.push_back(1);
v1.push_back(2);
v1.push_back(3);
就存放了1 2 3三个数据到v1对象中
5.2 insert 指定位置插入数据
 第一种:   (返回值是iterator,在之后讲迭代器失效会提到)
选择一个位置(迭代器),选择要插入的数据。
#include <iostream>
#include <vector>
using namespace std;
int main()
{
    vector<int> v1; // 创造一个空容器
    v1.push_back(1);
    v1.push_back(2);
    v1.push_back(3);
    v1.push_back(4);
    v1.insert(v1.begin(), 100); // 我们再开头插入一个元素100
    vector<int>::iterator it = v1.begin();
    while (it != v1.end())
    {
        cout << *it << " ";
        it++;
    }
	return 0;
} 
![]()
第二种:
选择一个位置(迭代器),选择要插入的个数,选择要插入的数据。
#include <iostream>
#include <vector>
using namespace std;
int main()
{
    vector<int> v1; // 创造一个空容器
    v1.push_back(1);
    v1.push_back(2);
    v1.push_back(3);
    v1.push_back(4);
    v1.insert(v1.begin(), 10, 5); // 我们头插100个5
    vector<int>::iterator it = v1.begin();
    while (it != v1.end())
    {
        cout << *it << " ";
        it++;
    }
	return 0;
} 
 ![]()
删:
5.2 pop_back 从尾部删除一个数据
#include <iostream>
#include <vector>
using namespace std;
int main()
{
    vector<int> v1; // 创造一个空容器
    v1.push_back(1);
    v1.push_back(2);
    v1.push_back(3);
    v1.push_back(4);
    for (auto u : v1)
    {
        cout << u << ' ';
    }
    cout << endl;
    v1.pop_back();
    v1.pop_back();
    v1.pop_back();
    for (auto u : v1)
    {
        cout << u << ' ';
    }
    cout << endl;
	return 0;
} 

5.3 erase 指定位置删除数据

注意这里的返回值是iterator,在之后讲迭代器失效的时候会提到。
第一种:删除一个指定位置的元素
第二种:删除一段区间的元素
// erasing from vector
#include <iostream>
#include <vector>
int main ()
{
  std::vector<int> myvector;
  // set some values (from 1 to 10)
  for (int i=1; i<=10; i++) myvector.push_back(i);
  // erase the 6th element
  myvector.erase (myvector.begin()+5);
  std::cout << "myvector contains:";
  for (unsigned i=0; i<myvector.size(); ++i)
    std::cout << ' ' << myvector[i];
  std::cout << '\n';
  // erase the first 3 elements:
  myvector.erase (myvector.begin(),myvector.begin()+3);
  std::cout << "myvector contains:";
  for (unsigned i=0; i<myvector.size(); ++i)
    std::cout << ' ' << myvector[i];
  std::cout << '\n';
  return 0;
} 

查:
5.4 find
find函数可以找到容器中我们要寻找的值 并且返回迭代器
find函数有三个参数 迭代器左区间 迭代器右区间 要查找元素 (左闭右开)适用于所有的迭代器。
为什么string类里面要自己设计一个find函数呢?因为要满足对各种字符串查找的需求。
注意!!!!!!
这里的find函数的头文件是 algorithm !!!!!!!!!
一定要记住! 不是容器的默认函数
#include <iostream>
#include <vector>
#include<algorithm>//为了能够使用find函数
using namespace std;
int main()
{
    vector<int> v1; // 创造一个空容器
    v1.push_back(1);
    v1.push_back(2);
    v1.push_back(3);
    v1.push_back(4);
    // v1.erase(v1.begin(), v1.begin()+2);
    vector<int>::iterator pos = find(v1.begin(), v1.end(), 3);  // 找到3
    v1.erase(pos); // 删除3
    vector<int>::iterator it = v1.begin();
    while (it != v1.end())
    {
        cout << *it << " ";
        it++;
    }
	return 0;
} 
 ![]()
改:
operator=
// vector assignment
#include <iostream>
#include <vector>
using namespace std;
int main()
{
	std::vector<int> foo(3, 1);
	std::vector<int> bar(5, 2);
	bar = foo;
	foo = std::vector<int>();
    //打印foo中的元素
	for (auto u : foo)
	{
		cout << u << " ";
	}
	cout << endl;
	std::cout << "Size of foo: " << int(foo.size()) << '\n';
    //打印bar中的元素
	for (auto u : bar)
	{
		cout << u << " ";
	}
	cout << endl;
	std::cout << "Size of bar: " << int(bar.size()) << '\n';
	return 0;
} 
 
这个运算符会改变size大小和内部的变量
七、迭代器失效:
在使用insert和earse的时候,会出现迭代器失效的问题。
7.1 实例1:
#include <iostream>
#include <vector>
using namespace std;
void test_1()
{
    vector<int> vec;
    vec.push_back(0);
    vec.push_back(1);
    vec.push_back(2);
    vec.push_back(3);
    vec.push_back(4);
    cout << vec.size() << ":" << vec.capacity() << endl;
    vector<int>::iterator it = vec.begin();
    while (it != vec.end())
    {
        if (*it % 2 == 0)
        {
            vec.insert(it, 5);   //这里会出现错误
            ++it;
        }
        ++it;
    }
    cout << vec.size() << ":" << vec.capacity() << endl;
    for (auto e : vec)
    {
        cout << e << " ";
    }
    cout << endl;
}
int main()
{
    test_1();
    return 0;
} 
分析:
vec.insert(it, 5); //这里会出现错误 当你把it传进去的时候,是传值传进去的,
它在insert内部会进行扩容(异地扩容)操作。内部的pos指针还指向原地,就不再准确了。
就会造成野指针问题。
而如果传引用传递的话,会让其他一些函数不能使用。
原因:insert之后vector会分配新的空间,旧迭代器便会失效。
解决方法:取insert函数的返回值为新的迭代器。
用图片来帮助理解:
      
         
改法:(接收新的it作为返回值)
while (it != vec.end())
{
    if (*it % 2 == 0)
    {
        it=vec.insert(it, 5);//这样子写是错误的,应该怎么改?
        it++;
    }
    ++it;
    
} 

7.2 实例2:
#include <iostream>
#include <vector>
using namespace std;
int main()
{
    vector<int> v1; // 创造一个空容器
    v1.push_back(1);
    v1.push_back(2);
    v1.push_back(3);
    v1.push_back(4);
    v1.push_back(5);
    v1.push_back(6);
    vector<int>::iterator it = find(v1.begin(), v1.end(), 2); // 意思是我们要删除2
    v1.erase(v1.begin()); //删除头元素
    v1.erase(it); //删除元素2
    return 0;
} 

调试看看:

 咦?你看到了没有,it指针指向的位置还是那个位置,但是里面的值却改变了。
删除头元素,就是把数据向前挪动,而it指针指向不变。导致后面出问题了。
解决方法:同样在使用完erase之后,拿it去接收erase函数的返回值
7.3 总结(重点)
1. insert和erase都会导致迭代器失效,我们都需要用it来接收新的迭代器。
it=insert(it,5); //在it位置插入元素5,拿it来接收新迭代器
it=erase(it);//删除it位置的元素,拿it来接收新迭代器
2. insert的返回值的it -->指向新插入的元素
erase的返回值的it -->指向删除元素的下一个元素
本篇内容到此结束!感谢大家阅读!!!






















