C++学习之STL学习:vector类的使用
之前我们了解到了string类的使用并通过对string常用接口的模拟实现对其底层有更深刻的了解。接下来我们将进一步深入学习STL类的下一个区:vector
vector在某些方面类似于顺序表的功能
作者的gitee主页在这里,感兴趣的可以自行查看:楼田莉子 (riko-lou-tian) - Gitee.com
目录
vector类的初始
vector的构造
编辑
初始化列表形态初始化
vector的析构
vector的赋值重载
vector的迭代器
正向迭代器
反向迭代器
const正向迭代器
const反向迭代器
vector的遍历方式
operator[]
at
front编辑
back
data
vector的容器操作
size
max_size
capacity
empty
resize
reserve
shrink_to_fit
vector与string的区别
vector增删查改
push_back
pop_back
find
insert
编辑 erase
swap
clear 编辑
emplace编辑
emplace_back
更好的遍历方式:结构化绑定
assign
vector的非成员函数重载
vector的关系运算符重载
编辑 swap函数
内存池函数
模板专用化
成员类:引用
成员函数:
flip
swap
类模板特化
一些题目:
杨辉三角
只出现一次的数字1
vector类的初始
官方文档:
vector::begin - C++ 参考
std::vector - cppreference.com
使用vector类之前必须包含头文件<vector>和
using namespace std
vector的构造
构造函数 | 接口功能说明 |
vector()(重点) | 无参构造 |
vector(size_type n,const value_type&val=value_type()) | 构造并初始化n个val |
vector(const vector& x)(重点) | 拷贝构造 |
vector(Inputlterator first,Inputlterator last) | 使用迭代器进行初始化构造 |
void test1()
{//无参数构造vector<int> v1;//v1的尾插元素v1.push_back(1);v1.push_back(2);v1.push_back(3);v1.push_back(4);//有参数构造vector<int> v2(5, 0);//初始化了5个值为0的元素//利用迭代器区间初始化vector<int>v3(v1.begin(), v1.end());
}
初始化列表形态初始化
//初始化列表初始化
vector<int>v1{ 1,2,3,4,5,6,7,8,9,10 };
vector<int>v2 = { 1,2,3,4,5,6,7,8,9,10 };
vector<int>v3({1,2,3,4,5,6,7,8,9,10});
vector的析构
vector的赋值重载
vector的迭代器
迭代器使用 | 接口说明 |
begin+end(重点) | 获取第一个数据位置的迭代器或者const迭代器,获取最后一个数据的下一个位置的迭代器 |
rbegin+rend | 获取最后一个数据位置的逆置迭代器或者const逆置迭代器,获取第一个数据前一个位置上的逆置迭代器 |
同string一样,vector 也有四组共8个迭代器。
正向迭代器
反向迭代器
const正向迭代器
const反向迭代器
vector的遍历方式
vector的遍历方式 | 功能 |
operator[] | 返回对容器中位置n处元素的引用 |
at | 返回对容器中位置n处元素的引用 |
front | 返回第一个元素的引用 |
back | 返回最后一个元素的引用 |
data | 指向内部数据的一个指针 |
迭代器 | 上面讲过了 |
范围for | C++11支持更简洁的范围for新遍历方式 |
operator[]
operator[]有边界检查,通过断言检查
at
at功能与operator相似,会主动检查边界并通过抛异常报错
front
返回第一个元素
back
返回最后一个元素
data
返回底层的指针。
vector的容器操作
容量空间 | 接口说明 |
size | 获取数据个数 |
max_size | 获取向量可容纳的最大值 |
capacity | 获取容量大小 |
empty | 判断容器是否为空 |
resize | 改变vector的size |
reserve | 改变vector的capacity |
shrink_to_fit | 获得容器剩余可以容纳的最大元素个数 |
capacity的代码在vs和g++下分别运行会发现,vs下capacity是按1.5倍增长的,g++是按2倍增长的。
这个问题经常会考察,不要固化的认为,vector增容都是2倍,具体增长多少是根据具体的需求定义的。
vs是PJ版本STL,g++是SGI版本STL。
reserve只负责开辟空间,如果确定知道需要用多少空间,reserve可以缓解vector增容的代价缺陷问题。resize在开空间的同时还会进行初始化,影响size。
size
max_size
同string一样,max_size仅仅作为理论值而存在
capacity
empty
resize
resize主要针对size进行扩容
功能:
1扩容(空余位置会进行插入数据)
2空间够直接会插入数据
3空间不够的时候删除数据
reserve
reserve针对于capacity进行扩容
当我知道你需要多少空间的时候提前预留空间可以减少扩容
shrink_to_fit
缩容。一种时间换空间的方式,一般情况下不用
vector与string的区别
如上图所示,在底层上,string与vector是很相似的。string底层是数组,vector也是数组
但是vector<char>是不能代替string的。尽管也有vector<string>的写法,任意类型纯可以存储到vector里面
但是vector末尾没有‘\0’,写字符串的时候无法与C语言兼容,也就无法替代string
vector增删查改
vector增删查改 | 接口的说明 |
push_back | 尾插 |
pop_back | 尾删 |
find | 查找(算法模块的,不是vector接口) |
insert | 在pos位置之前插入val |
erase | 删除pos位置的数据 |
swap | 交换两个vector的数据 |
operator[] | 像数组一样访问vector |
emplace | 构造并插入元素 |
emplace_back | 末尾构造并插入新元素 |
clear | 清清除容器所有的大小并使得容器大小为0 |
assign | 向向量分配新的内容,替换当前内容并分配新的空间 |
push_back
尾部插入数据
#include <iostream>
#include <vector>
#include <string>
using namespace std;
void test3()
{string s1 = ("hello world");vector<string> v1;v1.push_back(s1);v1.push_back("goodbye");//用范围for遍历更加方便for (auto s : v1){cout << s << " ";}cout << endl;}
int main()
{test3();return 0;
}
结果为:
但是这种写法效率比较低。因为这样会有string的拷贝构造,而且还是深拷贝
#include <iostream>
#include <vector>
#include <string>
using namespace std;
void test3()
{string s1 = ("hello world");vector<string> v1;v1.push_back(s1);v1.push_back("goodbye");//用范围for遍历更加方便for (const auto& s : v1){cout << s << " ";}cout << endl;}
int main()
{test3();return 0;
}
push_back构造方式:
void test5()
{vector<Boogiepop>v1;Boogiepop b1(1, 2);//有名对象v1.push_back(b1);//匿名对象v1.push_back(Boogiepop(1, 2));//多参数隐式类型转换v1.push_back({1,3});}
pop_back
尾部删除数据‘
find
vector和list的find函数都是通过算法库来查找的,使用的时候需要包含头文件<algorithm>
但是为什么string不用find函数而是自己要做一个呢?因为string的更加复杂
insert
erase
以上两个函数应用的代码举例
void test2()
{vector<int> v1(5, 0);v1.insert(v1.begin() + 2, 3);for (size_t i = 0;i < v1.size();i++){cout << v1[i] << " ";}cout << endl;auto it = find(v1.begin(), v1.end(), 5);if (it != v1.end()){cout << "找到元素" << endl;}else{cout << "未找到元素" << endl;}v1.erase(v1.begin());for (size_t i = 0;i < v1.size();i++){cout << v1[i] << " ";}
}
结果为:
swap
clear 
emplace
emplace_back
include <iostream>
#include <vector>
#include <string>
using namespace std;
void test4()
{vector<int>v1{ 1,2,3,4,5,6,7,8,9,10 };v1.push_back(1);v1.emplace_back(1);for (const auto& i : v1){cout << i << " ";}cout << endl;
}
int main()
{test4();return 0;
}
emplace_bakc可以用以下方式构造
void test5()
{//有名对象v1.emplace_back(b1);//匿名对象v1.emplace_back(Boogiepop(1, 2));//多参数隐式类型转换不能用(涉及到底层,比较麻烦)//但是可以用这种方式构造v1.emplace_back(1, 3);//这样效率更高//直接构造,不是拷贝构造,传构造Boogpop的参数,效率更高
}
更好的遍历方式:结构化绑定
void test5()
{vector<Boogiepop>v1;Boogiepop b1(1, 2);//有名对象v1.emplace_back(b1);//匿名对象v1.emplace_back(Boogiepop(1, 2));//多参数隐式类型转换不能用(涉及到底层,比较麻烦)//但是可以用这种方式构造v1.emplace_back(1, 3);//这样效率更高vector<Boogiepop>::iterator it = v1.begin();/* while (it != v1.end()){cout << (*it)._a1<< " " << (* it)._a2<< endl;++it;}*///迭代器:更方便的方式while (it != v1.end()){cout << it->_a1 << " " << it->_a2 << endl;++it;}cout << endl;//C++11:范围forfor (const auto& b : v1){cout << b._a1 << " " << b._a2 << endl;}cout<<endl;//C++17:结构化绑定for (auto [x,y]:v1){cout<<x<<" "<<y<<endl;}
}
部分读者可能不支持结构化绑定的语法,可以这么修改
1首先右键项目,找到属性:
2.然后在此页面中找到C++ 语言标准,更改至17及其以上的版本(如果没有请更换更高级版本的编辑器)
上面的代码还可以加引用。
//C++17:结构化绑定
auto &[x,y] = b1;
for (auto &b1:v1)
{cout<<x<<" "<<y<<endl;
}
加引用减少了拷贝,效率更高了
也可以这么写
//C++17:结构化绑定for (auto &[x,y] : v1){cout<<x<<" "<<y<<endl;}
当你访问的是一个结构体或者类的时候可以用这种方式
assign
vector的非成员函数重载
vector的关系运算符重载
swap函数
内存池函数
模板专用化
成员类:引用
成员函数:
flip
swap
类模板特化
一些题目:
杨辉三角
题目:
C++ 写法:
只出现一次的数字1
题目:
答案:
关于vector的使用目前我们就讲到这里,后续我们将进一步学习vector的底层模拟实现。敬请期待。
在这里各位读者朋友点一个赞,谢谢
封面图自取: