STL -- vector的使用详解

vector

1.vector是一个顺序容器,是数组的升级版,可以改变大小,即动态增长的数组。
2.vector实质上就是将数组和方法封装的一个类。

vector的底层

1.vector的迭代器

(1)vector的迭代器就是一个原生指针,若数据类型为T,则其迭代器就是T*的指针。
(2)将T*重定义为iterator(即typedef T* iterator)。

2.vector的成员变量为三个指针:start、finish、endofstorage。

(1)start:表示有效数据的起始位置
finish:表示最后一个有效数据的下一个位置
endofstorage:表示当前有效空间的下一个位置
(2)因为vector的迭代器就是原生指针,所以利用迭代器,此时其三个成员变量就有如下定义:

iterator start;     //T* start
iterator finish;    //T* finish
iterator endofstorage;    //T* endofstorage

3.vector内部通过一个expand( )函数进行扩容(该函数我们无法调用,但我们能通过resize和reserve进行扩容)
(1)当finish == endofstorage时需要扩容,一般扩大为原来容量的2倍。(扩大的倍数是可以改变的,但是一般不要太大,否则空间会浪费很多,但是也不能太小,否则就会频繁的扩容);
(2)扩容时,会开一段新的空间再把以前的数据拷下来(这里也会存在深浅拷贝);
(3)再释放旧的空间,然后再将新空间给我。

vector的初始化方式

vector<int> v1;   //v1是一个空的vector

vector<int> v2 = v1;   //v2是v1拷贝构造的

vector<int> v3(v1);    //v3也是v1拷贝构造的

vector<int> v4 = { 1, 2, 3, 4 };    //v4包含4个元素

vector<int> v5(10);    //v5包含10个元素,每个元素的数值为0

vector<int> v6(10, 2);   //v6包含10个元素,每个元素的数值为2

vector<int> v7{ 1, 2, 3, 4 };   //与v4的内容一致,包含4个元素


//采用列表初始化,则花括号里面的类型必须与vector给定的类型一致,如果不一致则不是列表初始化,编译器会尽量想办法按照类型初始化,如果不能就会出错

vector<string> v8{ 10 };    //因为vector里面的类型为string,而{}里面是int类型的数据,则不是初始化列表,而是v8里面含有10个空字符串

vector<string> v9{ 10, "11" };  //v9里面包含10个字符串“11”

vector的resize和reserve

  • resize和reserve都可以用来扩容,但是他们之间会有一些区别
1.resize

(1)用来改变size(size表示有效数据的个数)

void resize (size_type n, value_type val = value_type());

(2)用法:
①当n大于以前的size,则会将capacity和size都会增加至n

int main()
{
      vector<int> v = { 1, 2, 3, 4 };
      v.resize(10);   //此时v的size和capacity都会变为10,并且后面的数据默认初始化为0,即v里面的数据为1 2 3 4 0 0 0 0 0 0
      system("pause");
      return 0;
}

②如果n小于以前的size,则只会将size改变并不会改变capacity.

int main()
{
      vector<int> v = { 1, 2, 3, 4 };
      v.resize(2);   //此时v的size是2,capacity为4,并且v里面的数据为1 2
      system("pause");
      return 0;
}
2.reserve

(1)用来改变容量

void reserve (size_type n);

(2)用法:
①如果n大于以前的size,只会将capacity改变,但不改变size

int main()
{
      vector<int> v = { 1, 2, 3, 4 };
      v.reserve(10);   //此时v的size为4,capacity为10,v里面数据仍为1 2 3 4
      system("pause");
      return 0;
}

②如果n小于以前的size,则size和capacity都不会改变

int main()
{
      vector<int> v = { 1, 2, 3, 4 };
      v.reserve(2);  //此时v的size和capacity都不变,仍为4,则v里面的数据仍为1 2 3 4
      system("pause");
      return 0;
}
3.resize和reserve的区别与联系

(1)resize和reserve都是用来开空间的函数,但是必须提前知道所开 空间的大小,两个底层调用expand函数进行扩容;
(2)resize不仅可以可以将容量扩大,还可以改变size,而且还会对数据进行初始化,如果不给初始化的值,默认按照缺省类型进行初始化;如果给定初始化的值,就会按照自己给定的值进行初始化;reserve只是将容量扩大了,但是并没有改变size,即数据个数不变,一般配合push_back使用。
(3)当n(传的参数)大于size时,无论是resize和reserve都会增加capacity;当n小于size时,无论是resize还是reserve都不改变capacity,但是resize还会将size缩减至n。

vector的相关接口

  • vector位于头文件<vector>里,当使用vector的相关接口必须包含该头文件。
  • vector不支持头上的插入和删除,因为头上的插入和删除效率太低了,需要反复挪动数据,但是通过insert和erase也可以在头上进行插入和删除。
1.push_back

(1)用于尾上插入一个数据;

void push_back (const value_type& val);

(2)使用:

#include<iostream>
using namespace std;

#include<vector>

int main()
{
      vector<int> v;
      v.push_back(1);
      v.push_back(2);    //这步完成后,v里面的数据为1 2
}
2.pop_back

(1)用于删除尾上的数据

void pop_back();

(2)使用:

#include<iostream>
using namespace std;

#include<vector>

int main()
{
      vector<int> v;
      v.push_back(1);
      v.push_back(2);   //此时v里面的数据为1 2
      v.pop_back();     //此时v里面的数据为1
}
3.insert

(1)用于在pos位置前面插入数据,可以插入一个也可以插入n个还可以插入一段迭代器区间中的元素。
返回值:返回一个迭代器,该迭代器指向插入位置,或者说是插入的第一个有效数据所在位置

//1.在pos前面插入一个值
iterator insert (iterator position, const value_type& val);  

//2.在pos前面插入n个值
void insert (iterator position, size_type n, const value_type& val);   

//3.在pos前面插入一段迭代器区间
template <class InputIterator>
 void insert (iterator position, InputIterator first, InputIterator last); 

(2)使用

注意:插入位置必须在v.end()之前,可以包括v.end()

①在pos前面插入一个数

int main()
{
      vector<int> v;
      vector<int>::iterator it = v.begin();

      v.insert(it, 10);   //第一个参数为vector的迭代器,第二个参数是插入的数据的值,此时v里面就包含一个10
      system("pause");
      return 0;
}

②在pos前面插入n个数

int main()
{
      vector<int> v;
      vector<int>::iterator it = v.begin();

      v.insert(it,2, 10);   //此时v里面包含两个10
      system("pause");
      return 0;
}

③在pos前面插入一段迭代器区间(注意STL里面的迭代器区间全部都是左闭右开的区间)

  • 迭代器可以是同类vector自身的迭代器,也可以是与自己定义vector的类型一样的指针(例如vector<int>,则迭代器区间的类型可以是int*)

例1:在v1的begin()之前插入v2的第一到第三个数,由于迭代器是左闭右开的区间,所以v1里面只插入了v2的前两个数。

int main()
{
      vector<int> v1;
      vector<int> v2 = { 1, 2, 3, 4 };
      vector<int>::iterator it1 = v1.begin();
      vector<int>::iterator it2 = v2.begin();
      v1.insert(it1, it2, it2 + 2);    //此时v1里面包含 1  2
      system("pause");
      return 0;
}

例2:在pos前面插入一个数组里面的某个区间

int main()
{
      int a[] = { 1, 2, 4, 5, 6 };
      vector<int> v1;
      vector<int>::iterator it = v1.begin();
      v1.insert(it, a, a + 3);   //此时v里面数据为  1  2  4
      return 0;
}

如果将一个double类型数组里面某个迭代器区间的值插入到一个存放int型数据的vector里面,则会将double类型转换为int再插入到vector里面。

4.erase

(1)用于删除某些元素,可以删除某个位置上的数据,也可以删除一段迭代器区间的数据

iterator erase (iterator position);
iterator erase (iterator first,  iterator last);

(2)使用:
①删除某个位置上的数据

int main()
{
      vector<int> v1 = { 1, 2, 3, 4 };
      vector<int>::iterator it= v1.erase(v1.begin() + 1);  //此时v里面的数据变为 1  3  4
      cout << *it << endl;   //此时it指向3所在的位置
      system("pause");
      return 0;
}

注意:删除元素可能会导致迭代器失效

int main()
{
      vector<int> v1 = { 1, 2, 3, 4 };
      vector<int>::iterator it= v1.erase(v1.begin() + 3);  //将4删除了,但是迭代器会指向4的下一个位置,但是4已经是最后一个位置,则4的洗一个位置就是v1.end(),但是这个位置不是有效位置,再想解引用it,则会发生越界行为

      cout << *it << endl;   //越界
      system("pause");
      return 0;
}

②删除迭代器区间的某些数据

int main()
{
      vector<int> v1 = { 1, 2, 3, 4 };
      vector<int>::iterator it= v1.erase(v1.begin(),v1.begin()+2);   //此时v里面只包含 3  4,并且it指向的3所在的那个位置
      system("pause");
      return 0;
}

vector中访问某个有效数据的相关接口

1.operator[ ]

(1)用于访问某个位置上的数据

reference  operator[] (size_type  n);
const_reference  operator[] (size_type  n) const;
//返回值:返回当前位置数据的引用,表示可读可改,const_reference是针对const版本的,只能读不能改

(2)使用:


int main()
{
      vector<int> v = { 1, 2, 3, 4 };
      cout << v[0] << endl;   //输出1

      v[0] = 10;
      cout << v[0] << endl;   //将v[0]修改之后,此时输出10
      system("pause");
      return 0;
}
2.at

(1)at与operator[ ]的接口完全一致,都是返回引用;只是用法上稍有区别,只是operator[ ]重载了[ ],所以可以像数组一样访问。

reference  at (size_type  n);
const_reference  at (size_type  n) const;

(2)使用:

int main()
{
      vector<int> v = { 1, 2, 3, 4 };
      cout << v.at(0) << endl;   //输出1

      v.at(0) = 10;
      cout << v.at(0) << endl;   //输出10
      system("pause");
      return 0;
}

(3)operator[ ] 与at的区别
operator[ ] 如果越界访问,会出现断言错误;而at是抛异常,且会调用abort函数,使得自己异常终止。

3.front

(1)用于:取第一个数据

reference front();
const_reference front() const;

(2)使用:

int main()
{
      vector<int> v = { 1, 2, 3, 4 };
      cout << v.front() << endl;   //输出 1
      system("pause");
      return 0;
}
4.back

(1)用于:取最后一个数据

reference back();
const_reference back() const;

(2)使用:

int main()
{
      vector<int> v = { 1, 2, 3, 4 };
      cout << v.back() << endl;    //输出 4
      system("pause");
      return 0;
}

遍历vector的两种方式

1.通过for循环
int main()
{
      vector<int> v = { 1, 2, 3, 4 };
      for (size_t i = 0; i < v.size(); ++i)
      {
           cout << v[i] << " ";
      }
      system("pause");
      return 0;
}
2.通过迭代器
int main()
{
      vector<int> v = { 1, 2, 3, 4 };
      vector<int>::iterator it = v.begin();
      while (it != v.end())
      {
           cout << *it << " ";
           ++it;
      }
      system("pause");
      return 0;
}

猜你喜欢

转载自blog.csdn.net/xu1105775448/article/details/80633505