文章目录
vector容器
概念与构造
动态扩展:
- 并不是在原空间之后续接新空间,而是找更大的内存空间,然后将原数据拷贝新空间,释放原空间
因为不能确保原来的空间后面是否有别的程序在用
对外提供的接口
前端封闭,尾部插入数据
push_back() 尾插
pop_back() 尾删
迭代器:
vector 提供了如下几种 迭代器
-
begin()/cbegin()
返回指向首元素的迭代器,其中 *begin = front。 -
end()/cend()
返回指向数组尾端占位符的迭代器,注意是没有元素的。 -
rbegin()/crbegin()
返回指向逆向数组的首元素的逆向迭代器,可以理解为正向容器的末元素。 -
rend()/crend()
返回指向逆向数组末元素后一位置的迭代器,对应容器首的前一个位置,没有元素。
以上列出的迭代器中,含有字符 c 的为只读迭代器,不能通过只读迭代器去修改 vector 中的元素的值。如果一个 vector 本身就是只读的,那么它的一般迭代器和只读迭代器完全等价。只读迭代器自 C++11 开始支持。
迭代器的随机访问:
vector容器的迭代器是支持随机访问的。指的是可以跳好几个访问。
关于随机访问的补充:STL中支持随机访问的迭代器
vector
容器的迭代器(iterator)是C++ STL(Standard Template Library)中提供的一种工具,用于访问和遍历vector
容器中的元素。迭代器提供了一种抽象化的方法,可以在不关心底层实现的情况下访问数据。迭代器可以理解为指向容器中元素的指针,可以通过迭代器进行元素的读取、修改等操作。
#include <iostream>
#include <vector>
int main() {
// 创建一个包含整数的vector
std::vector<int> vec = {1, 2, 3, 4, 5};
// 声明一个迭代器,指向vector容器的开始
std::vector<int>::iterator it;
// 使用迭代器遍历vector中的元素,并输出
//这里就是输出打印
for (it = vec.begin(); it != vec.end(); ++it) {
std::cout << *it << " ";
}
// 输出结果:1 2 3 4 5
return 0;
}
函数原型:
vector<T> v;
//采用模板实现类实现,默认构造函数vector(v.begin(), v.end());
//将v[begin(), end())区间中的元素拷贝给本身。vector(n, elem);
//构造函数将n个elem拷贝给本身。vector(const vector &vec);
//拷贝构造函数。
由于v.end()是指向最后一个元素的下一个,所以第二条的区间是左闭右开。
容器的构造与打印输出
#include <vector>
void printVector(vector<int>& v) { //将容器传进来打印。注意是引用传递
for (vector<int>::iterator it = v.begin(); it != v.end(); it++) {
//用迭代器访问和遍历vector容器的元素
cout << *it << " ";
}
cout << endl;
}
void test01()
{
//默认构造,无参构造
vector<int> v1;
for (int i = 0; i < 10; i++)
{
v1.push_back(i);
}
printVector(v1);
//区间方式构造
vector<int> v2(v1.begin(), v1.end());//第二种构造,把两个迭代器传进去,进行构造
printVector(v2);
//n个elem构造
vector<int> v3(10, 100);//这里的v3就是10个100,第一个是个数。第二个是赋值
printVector(v3);
//拷贝构造
vector<int> v4(v3);
printVector(v4);
//默认构造和拷贝构造用的比较多
}
赋值
功能描述:
- 给vector容器进行赋值
赋值常见操作
-
vector& operator=(const vector &vec);
//重载等号操作符 -
assign(beg, end);
//将[beg, end)区间中的数据拷贝赋值给本身。 -
assign(n, elem);
//将n个elem拷贝赋值给本身。
#include <vector>
void printVector(vector<int>& v) {
for (vector<int>::iterator it = v.begin(); it != v.end(); it++) {
//这一行多敲几遍
cout << *it << " ";
}
cout << endl;
}
//赋值操作
void test01()
{
vector<int> v1; //无参构造,写入v1数据
for (int i = 0; i < 10; i++)
{
v1.push_back(i);
}
printVector(v1);
//等号赋值,此时v2和v1一模一样
vector<int>v2;
v2 = v1;
printVector(v2);
//另一种赋值assign,提供两个迭代器,注意取到的区间是左闭右开,end的值不会取到
vector<int>v3;
v3.assign(v1.begin(), v1.end());
printVector(v3);
//n个elem的方式
vector<int>v4;
v4.assign(10, 100);
printVector(v4);
}
int main() {
test01();
system("pause");
return 0;
}
容量和大小
容量操作接口
-
empty();
//判断容器是否为空 -
capacity();
//容器的容量 -
size();
//返回容器中元素的个数,注意容器中最后一个元素的下标是**vector.size()-1
** -
resize(int num);
//重新指定容器的长度为num,若容器变长,则以默认值填充新位置。 //如果容器变短,则末尾超出容器长度的元素被删除。
-
resize(int num, elem);
//重新指定容器的长度为num,若容器变长,则以elem值填充新位置。 //如果容器变短,则末尾超出容器长度的元素被删除
capacity
永远>=size
#include <vector>
void printVector(vector<int>& v) { //打印函数
for (vector<int>::iterator it = v.begin(); it != v.end(); it++) {
//这一句多写几遍
cout << *it << " ";
}
cout << endl;
}
void test01()
{
//数据写入v1
vector<int> v1;
for (int i = 0; i < 10; i++)
{
v1.push_back(i);
}
printVector(v1);
if (v1.empty())
{
cout << "v1为空" << endl;
}
else
{
//不为空:看容量和大小
cout << "v1不为空" << endl;
cout << "v1的容量 = " << v1.capacity() << endl;
cout << "v1的大小 = " << v1.size() << endl;
//容量永远>=大小,此处容量是13,大小是10,13还可动态扩展
}
//resize 重新指定大小
//若指定的更大,则默认用0填充新的位置
//不想用0的话,可以假加上一个参数来填充,比如下面就是10来填充
v1.resize(15,10);
printVector(v1);
//resize 重新指定大小
//若指定的更小,超出部分元素被删除
v1.resize(5);
printVector(v1);
}
int main() {
test01();
system("pause");
return 0;
}
resize会用0来进行填充
总结
- 判断是否为空 — empty
- 返回元素个数 — size
- 返回容器容量 — capacity
- 重新指定大小 — resize
插入和删除
功能描述:
- 对vector容器进行插入、删除操作
插入/删除接口
push_back(ele);
//尾部插入元素elepop_back();
//删除最后一个元素insert(const_iterator pos, ele);
//迭代器指向位置pos插入元素eleinsert(const_iterator pos, int count,ele);
//迭代器指向位置pos插入count个元素eleerase(const_iterator pos);
//删除迭代器指向的元素erase(const_iterator start, const_iterator end);
//删除迭代器从start到end之间的元素clear();
//删除容器中所有元素
erase()函数是**O(n)
的操作,是要把后面元素整体前移,做覆盖操作的**。
注意插入的时候不能直接使用一号位置/二号位置,需要使用迭代器进行插入
#include <vector>
void printVector(vector<int>& v) {
for (vector<int>::iterator it = v.begin(); it != v.end(); it++) {
//打印,多写几遍
cout << *it << " ";
}
cout << endl;
}
//插入和删除
void test01()
{
vector<int> v1;
//插入操作:尾插
v1.push_back(10);
v1.push_back(20);
v1.push_back(30);
v1.push_back(40);
v1.push_back(50);
printVector(v1);
//删除操作:尾删
v1.pop_back();
printVector(v1);
//插入操作2:自己提供迭代器,第一个参数是迭代器
//begin是头部,最开始多一个100
v1.insert(v1.begin(), 100);
printVector(v1);
//或者在头部插入n个elem,这句话在头部插入两个1000
v1.insert(v1.begin(), 2, 1000);
printVector(v1);
//删除,参数也是迭代器
v1.erase(v1.begin());
printVector(v1);
//删除操作2:提供区间,如果区间是begin和end,就相当于清空。
v1.erase(v1.begin(), v1.end());
//清空操作,与上一句一样效果
v1.clear();
//打印
printVector(v1);
}
总结
- 尾插 — push_back
- 尾删 — pop_back
- 插入 — insert (位置迭代器)
- 删除 — erase (位置迭代器)
- 清空 — clear
数据存取
功能描述:
- 对vector中的数据的存取操作
数据存取接口:
at(int idx);
//返回索引idx所指的数据operator[];
//返回索引idx所指的数据front();
//返回容器中第一个数据元素back();
//返回容器中最后一个数据元素
#include <vector>
void test01()
{
vector<int>v1;
for (int i = 0; i < 10; i++)
{
v1.push_back(i);
}
//利用中括号方式访问数组元素
for (int i = 0; i < v1.size(); i++)
{
cout << v1[i] << " ";
}
cout << endl;
//利用成员函数at来进行访问
for (int i = 0; i < v1.size(); i++)
{
cout << v1.at(i) << " ";
}
cout << endl;
//获取第一个元素
cout << "v1的第一个元素为: " << v1.front() << endl;
//获取最后一个元素
cout << "v1的最后一个元素为: " << v1.back() << endl;
}
int main() {
test01();
system("pause");
return 0;
}
总结:
- 除了用迭代器获取vector容器中元素,[ ]和at也可以
- front返回容器第一个元素
- back返回容器最后一个元素
互换容器
功能描述:
- 实现两个容器内元素进行互换
互换接口
swap(vec);
// 将vec与本身的元素互换
#include <vector>
void printVector(vector<int>& v) {
for (vector<int>::iterator it = v.begin(); it != v.end(); it++) {
//打印容器内容
cout << *it << " ";
}
cout << endl;
}
void test01()
{
vector<int>v1;
for (int i = 0; i < 10; i++)
{
v1.push_back(i);
}
printVector(v1);
//写入v1容器内容并打印
//v1是0 1 2 3 4 5 6 7 8 9
//写入v2容器内容并打印
//v2是10 9 8 7 6 5 4 3 2 1,此处并不是倒着往里面写,是本身的i就是倒序的
vector<int>v2;
for (int i = 10; i > 0; i--)
{
v2.push_back(i);
}
printVector(v2);
//互换容器
cout << "互换后" << endl;
v1.swap(v2);
//互换之后打印
printVector(v1);
printVector(v2);
}
//2.实际用途:swap可以用来收缩内存空间
void test02()
{
//放了100000数据进去
vector<int> v;
for (int i = 0; i < 100000; i++) {
v.push_back(i);
}
//此时,大小是10万个数据,容量是13万
cout << "v的容量为:" << v.capacity() << endl;
cout << "v的大小为:" << v.size() << endl;
//再重新指定容器大小,也就是大小变成3
v.resize(3);
//此时,容量13万没变,大小变成3
cout << "v的容量为:" << v.capacity() << endl;
cout << "v的大小为:" << v.size() << endl;
//此时内存就发生了浪费,使用swap收缩内存
//收缩内存
vector<int>(v).swap(v); //匿名对象
cout << "v的容量为:" << v.capacity() << endl;
cout << "v的大小为:" << v.size() << endl;
}
收缩内存解释
//收缩内存
vector<int>(v).swap(v); //匿名对象
前提是容量远大于大小,容量13万,大小是3。
vector<int>(v)
是匿名对象,是新的对象,没有名称,按照v(也就是已定义的那个容器)来给该对象进行初始化操作,按照v目前所用的元素个数,来初始化匿名对象大小。
因此,这个匿名对象初始化后容量是3,大小也是3。
(因为v容器现在所用的元素个数就是3,而不是13万)
.swap(v)
的操作其实相当于容器的交换,v和匿名对象进行交换,如下图所示。
匿名对象特点,这行代码执行完毕后,编译器发现是匿名对象会立刻回收。
那么浪费的内存会直接被系统回收掉,完成了收缩内存,将浪费的内存回收给系统。
总结
swap可以使两个容器互换,可以达到实用的收缩内存效果
预留空间
功能描述:
- 减少vector在动态扩展容量时的扩展次数
预留空间接口
reserve(int len);
//容器预留len个元素长度,预留位置不初始化,元素不可访问。- 区分
reserve
和reverse
。
#include <vector>
void test01()
{
vector<int> v;
//如果本来就知道要放多少数据,可以利用reserve预留空间,防止重复动态拓展
v.reserve(100000);
int num = 0; //统计容器的新空间开辟次数
int* p = NULL;
//指针指向首地址,如果开辟新的内存,首地址会发生变化,累计数值会+1
for (int i = 0; i < 100000; i++) {
v.push_back(i);
//放10万个数进去
//指针指向首地址
if (p != &v[0])
{
p = &v[0];
num++; //开辟一次内存
}
}
cout << "num:" << num << endl;//累计开辟内存个数
}
运行结果:10万个数据开辟了30次内存。
总结
如果数据量较大,可以一开始利用reserve预留空间