购物网站导航素材代码,内蒙古自治区精神文明建设网站,wordpress插件连不上,女式包包网站建设策划书前言 本章主要内容为两个部分#xff1a; vector是什么#xff1f;vector常用接口的使用。 一、vector的介绍
vector是表示可变大小数组的容器就像数组一样#xff0c;vector也采用的连续空间来存储元素。也意味着可以采用下标对vector的元素进行访问#xff0c;和数组一样…前言 本章主要内容为两个部分 vector是什么vector常用接口的使用。 一、vector的介绍
vector是表示可变大小数组的容器就像数组一样vector也采用的连续空间来存储元素。也意味着可以采用下标对vector的元素进行访问和数组一样高效。但是又不像数组它的大小是可以动态改变的而且它的大小会被容器自动处理。本质上vector使用动态分配数组来存储它的元素。当新元素插入时这个数组需要被重新分配大小。为了增加存储空间其做法是分配一个新的数组然后将全部元素移到这个数组。就时间而言这是一个相对代价高的任务因此每当一个新的元素加入到容器的时候vector并不会每次都重新分配大小。vector分配空间策略vector会分配一些额外的空间以适应可能的增长因此存储空间比实际需要的存储空间更大。不同的库采用不同的策略权衡空间的使用和重新分配。但是无论如何重新分配都应该是对数增长的间隔大小以至于在末尾插入一个元素的时候是在常数时间的复杂度完成的。因此vector占用了更多的存储空间为了获得管理存储空间的能力并且以一种有效的方式动态增长。与其他动态序列容器相比dequelist and forward_listvector在访问元素的时候更加高效在末尾添加和删除元素相对高效。对于其他不在末尾的删除和插入操作效率更低。比起list和forward_list统一的迭代器和引用更好。
vector的文档链接
二、vector常用接口的使用
1、vector是一个类模板使用时需要显示实例化 tip
vector有两个模板参数 T元素的数据类型Alloc空间配置器用于定义存储分配模型的分配器对象的类型。默认情况下使用allocator类模板它定义了最简单的内存分配模型并且与值无关。 空间配置器即内存池STL中所有的容器都使用内存池因为容器需要频繁申请和释放空间为了提高效率所以使用内存池。allocator类模板是库里面实现的一个默认分配器如果没有指定最后一个模板参数所有标准容器都将使用这个分配器它是标准库中唯一的预定义分配器。一般我们都使用库中的这个默认分配器所以我们不需要显式实例化Alloc。使用类模板我们必须显式实例化。类模板的显式实例化类模板名字实例化的类型显式模板参数实参与模板参数的匹配 显式模板实参按由左至右的顺序与对应的模板参数匹配第一个模板实参与第一个模板参数匹配第二个实参与第二个参数匹配以此类推。注只有尾部最右参数的显式模板实参才可以忽略但前提是它们可以从函数参数推断出来或为缺省参数。
2、vector的构造函数 construcort构造函数声明接口说明explicit vector (const allocator_type alloc allocator_type());默认构造函数一般alloc空间配置器不用传参使用缺省值。即无参构造explicit vector (size_type n, const value_type val value_type(),const allocator_type alloc allocator_type());构造并初始化n个valvector (InputIterator first, InputIterator last,const allocator_type alloc allocator_type());使用迭代器进行初始化vector (const vector x);拷贝构造
代码示例
//vector的构造函数
void test_vector1()
{//1、无参构造vectorint v1;//构造一个空的int类型的vector对象//2、构造并初始化n个valvectorchar v2(10, a);//构造一个char类型的vector并且初始化10个字符avectorchar v3(5);//构造一个char类型的vector并且默认初始化5个字符//3、使用迭代器初始化//①使用自己类型的迭代器初始化vectorchar v4(v2.begin(), v2.end());//②使用其他类型的迭代器初始化vectorint v5(v2.begin(), v2.end());//③连续存储空间的指针也属于迭代器int arr[] { 1, 2, 3 };vectorint v6(arr, arr 3);//4、拷贝构造vectorint v7(v6);
}
tip
无参构造一般使用最多构造并初始化n个val val为缺省参数所以可以指定实参使用指定的实参初始化或不指定实参使用缺省值值初始化 val不指定实参库会创建一个值初始化的元素初值把它赋个容器中的元素。这个初值由vector对象中元素的类型决定如果vector对象的元素是内置类型比如int则元素默认初始化为0char则元素默认初始化为’\0’如果元素是某种类类型比如string则元素由类默认初始化 使用迭代器初始化构造 InputIterator模板参数意味着你传什么类型的迭代器他就实例化什么类型的迭代器初始化构造连续存储空间的指针也是迭代器迭代器区间左闭合区间[first,last) 拷贝构造用一个已经存在的对象初始化另一个对象类类型的隐式转换 能通过一个实参调用的构造函数定义了一条从构造函数的参数类型向类类型隐式转换的规则注每次只能执行一种类类型的转换explicit修饰构造函数禁止隐式类型转换
3、vector的遍历
接口名称接口说明operator[]下标[]像数组一样可以随机访问beginend获取第一个元素位置的iterator/const_iterator获取最后一个元素的下一个位置的iterator/const_iteratorrbeginrend获取最后一个元素位置的reverse_iterator获取第一个元素位置前一个位置的reverse_iterator范围forC11支持的语法糖只要支持迭代器就可以使用
代码示例
//vector的遍历
void test_vector2()
{vectorint v1;v1.push_back(1);v1.push_back(2);v1.push_back(3);v1.push_back(4);v1.push_back(5);//1、operator[]——使用下标[]for (size_t i 0; i v1.size(); i){cout v1[i] ;}cout endl;//2、使用迭代器//①正向遍历auto vit v1.begin();while (vit v1.end()){cout *vit ;vit;}cout endl;//①反向遍历auto rit v1.rbegin();while (rit v1.rend()){cout *rit ;rit;}cout endl;//3、有迭代器就支持范围forfor (auto e : v1){cout e ;}cout endl;
}tip
operator[] operator[]越界是断言处理operator[]通常用两个版本一个返回普通引用另一个是类的常量成员并且返回常量引用即一个可读可写版本一个只可读不可写的版本。函数重载调用会走最匹配的标准库类型限定使用的下标必须是size_t内置类型的下标不是无符号类型 迭代器 迭代器是通用的任何容器都支持迭代器并且用法类似算法可以通过迭代器去处理容器中的数据 范围forC11支持的语法糖只要支持迭代器就可以使用注范围for底层被替换为begin和end所以范围for只能正向遍历
4、vector的容量操作
接口名称接口说明size获取元素个数capacity获取容量大小empty判断是否为空reserve改变vector的capacityresize改变vector的size
1sizecapacityempty
void test_vector1()
{vectorint v(10);//构造一个int类型的数组并10个默认初始化的元素//获取vector对象的元素个数cout v.size() endl;//获取vector对象的容量cout v.capacity() endl;//判断vector对象是否为空cout v.empty() endl;//了解max_size——判断当前vector对象可以容纳的最大元素数在不同平台实现不一样并无实际意义cout v.max_size() endl;
}tip
size 获取vector中的元素个数的注这是vector中实际对象的数量不一定等于其存储容量 capacity 获取当前为vector分配的存储空间以元素表示capacity不一定等于size它可以大于或等于capacity不是固定的当此容量耗尽并需要更多容量时vector会自动扩容可以通过reserve显式改变capacity empty 判断是否为空为空返回true不为空返回false。size0即为空注此函数不会以任何方式修改容器。要清空vector的内容请使用clear 了解max_size返回当前vector对象可以容纳的最大元素数它是一个理论值所以无实际意义
2reserveresize
引入reserve和resize
//测试vector的默认扩容机制
void test_vector2()
{vectorint v;size_t sz v.capacity();cout making v grow:\n;for (int i 0; i 100; i){v.push_back(i);if (sz ! v.capacity()){sz v.capacity();cout capacity changed: sz endl;}}
}tip
vector使用动态分配数组来存储它的元素当这个数组的容量不够的时候vector会自动扩容。capacity的代码在VS和g下分别运行会发现VS下capacity是按1.5倍增长的g是按2倍增长的。 这个问题经常被考察不要固化的认为vector增容都是2倍具体增长多少是根据具体的需求定义的。VS是PJ版本STLg是SGI版本STL。扩容一般一次扩1.5倍或2倍因为扩1.5倍或2倍是平衡的做法单次扩容越多插入N个元素扩容次数越少效率就越高但是浪费空间单次扩容越少插入N个元素扩容次数越多效率就越低。
扩容是有代价的所以vector中提供两个接口reserve和resize可以避免多次扩容。 reserve请求改变vector的容量
//如果已经确定vector中要存储元素的大概个数可以提前将空间设置足够
//就可以避免插入数据多次扩容导致的效率低下的问题
void test_vector3()
{vectorint v;//已提前知道v中大概存储100个元素//所以可以提前将容量设置好避免插入数据多次扩容v.reserve(100);//1、reserve只是单纯的开空间不会影响字符串的长度和内容。cout v.size() endl;cout v.capacity() endl;//2、观察是否避免了扩容size_t sz v.capacity();cout making bar grow:\n;for (int i 0; i 100; i){v.push_back(i);if (sz ! v.capacity()){sz v.capacity();cout capacity changed: sz endl;}}//清空数据删除所有元素即size0v.clear();//clear不会影响capacitycout v.size() endl;cout v.capacity() endl;
}tip
reserve请求改变vector的capacity 如果 n 大于当前向量容量则该函数会导致容器重新分配其存储将其容量增加到 n或更大。在所有其他情况下函数调用不会导致重新分配并且vector容量不受影响。注意reserve只是单纯开空间不会影响vector的大小和内容。 clear清除内容 删除vector中的所有元素使size0注clear不会影响vector的容量
resize改变vector的size
void test_vector4()
{vectorint v;//1、nsizencapacity开空间并初始化v.resize(100);cout v.size() endl;cout v.capacity() endl;//2、nsize删除超出的元素v.resize(10);cout v.size() endl;cout v.capacity() endl;
}tip
resize将容器大小调整为n 如果 n 小于当前容器大小则内容将减少到其前 n 个元素删除超出的元素。如果 n 大于当前容器大小则通过在末尾插入所需数量的元素来扩展内容以达到 n 的大小。如果指定了 val则新元素将初始化为 val 的副本否则它们将进行值初始化。注意如果 n 也大于当前容器容量则会自动重新分配分配的存储空间会影响vector的容量如果ncapacity不会影响vector的容量。
5、vector的增删查改
接口名称接口说明push_back尾插pop_back尾删insert在position之前插入valerase删除position位置的数据find查找注意这个是算法模块实现不是vector的成员接口sort排序注意这个也是算法模块实现不是vector的成员接口
1push_backpop_back
void test_vector1()
{vectorint v;//尾插1 2 3 4v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);for (auto e : v){cout e ;}cout endl;//尾删v.pop_back();for (auto e : v){cout e ;}cout endl;
}tip
push_back 尾插在vector的末尾插入val每一次尾插后size1当且仅当新的向量大小超过当前向量容量时才会自动重新分配分配的存储空间。 pop_back尾删删除vector中的最后一个元素尾删之后size-1vector采用顺序表存储数据所以vector在访问元素的时候更加高效在末尾添加和删除元素相对高效。对于其他不在末尾的删除和插入操作效率更低。所以vector没有专门头插头删的接口但是有insert和erase。
2inserterase
void test_vector2()
{int arr[] { 8, 2, 3, 9, 7, 3, 5, 1, };vectorint v(arr, arr sizeof(arr) / sizeof(int));for (auto e : v){cout e ;}cout endl;//头插一个10v.insert(v.begin(), 10);//在下标2元素之前插入2个1v.insert(v.begin() 2, 2, 1);//尾插一个迭代器区间v.insert(v.end(), arr, arr 2);for (auto e : v){cout e ;}cout endl;//头删v.erase(v.begin());//删除一个迭代器区间的元素v.erase(v.begin(), v.begin() 3);for (auto e : v){cout e ;}cout endl;
}tip
insert insert是重载函数调用时编译器会找一个与实参最匹配的在position位置的元素之前插入val在position位置的元素之前插入n个val在position位置的元素之前插入一个迭代器区间的元素 erase erase是重载函数调用时编译器会找一个与实参最匹配的删除position位置的元素删除迭代器区间的元素注意迭代器区间为左闭合区间即[firstlast) 一般我们很少使用insert和erase因为在尾部之外的位置插入或删除元素效率低。
3find
说明
vector中并没有find这个find是算法algorithm中的。STL中把容器存数据和算法处理数据分开的通过迭代器将其关联。算法中的find是一个函数模板其功能是在一个迭代器区间[firstlast)中查找一个值找到了就返回它的迭代器范围中与该值相等的第一个元素的迭代器没找到就返回last。
void test_vector3()
{int arr[] { 8, 2, 3, 9, 7, 3, 5, 1, };vectorint v(arr, arr sizeof(arr) / sizeof(int));for (auto e : v){cout e ;}cout endl;//STL中容器和算法是分开的//容器存数据算法处理数据//他们之间通过迭代器关联//例如vector中没有find但是算法中有那我们可以通过迭代器将其关联auto pos find(v.begin(), v.end(), 3);if (pos ! v.end()){cout 找到了 endl;v.erase(pos);}for (auto e : v){cout e ;}cout endl;
}4sort
vector中也没有sort这个sort是算法algorithm中的。STL中把容器存数据和算法处理数据分开的通过迭代器将其关联。这里我们只是简单介绍一下sort的使用sort有两个版本 版本1默认升序less 对迭代器区间[firstlast)升序版本2降序需要自己再传一个仿函数库中实现了greater我们可以直接使用
void test_vector4()
{int arr[] { 8, 2, 3, 9, 7, 3, 5, 1, };vectorint v(arr, arr sizeof(arr) / sizeof(int));for (auto e : v){cout e ;}cout endl;//STL中容器和算法是分开的//容器存数据算法处理数据//他们之间通过迭代器关联//例如将vector的数据排序可以使用算法中的sort//1、sort默认为升序()sort(v.begin(), v.end());for (auto e : v){cout e ;}cout endl;//1、sort降序()//①使用仿函数sort(v.begin(), v.end(), greaterint());//②反向迭代器的升序即降序//sort(v.rbegin(), v.rend());for (auto e : v){cout e ;}cout endl;
}6、vector char 可以替代string
思考 vector的底层存储也是动态的顺序表那vector能替代string吗 不能有如下几点原因 从结构上string为了兼容C在每一个string对象之后自动添加了一个’\0’字符而vector char 需要我们自己手动添加从接口上string提供了很多对字符串的专用接口例如findsubstr……string只能存储字符类型而vector可以存储多种类型 总结string和vector是两种最重要的标准库类型各自有各自的价值前者支持可变长字符串后者则表示可变长的集合。 使用vector存储string
void test_vector2()
{//vector是一个模版只要有一个确定的类型就可以将其实例化出来vectorstring v;//尾插string对象//1、先定义一个string对象string name 张三;v.push_back(name);//2、匿名对象v.push_back(string(李四));//3、隐式类类型的转换v.push_back(王五);for (auto e : v){cout e endl;}
}tip
当函数参数类型为string类型时。有三种传参方式 先创建一个string对象再将string对象传过去传string的匿名对象直接转C字符串隐式类类型转换为string这三种传参方式我们常常使用匿名对象 类类型隐式转换 能通过一个实参调用的构造函数定义了一条从构造函数的参数类型向类类型隐式转换的规则注意编译器一次只能执行一种类类型的转换explicit修饰构造函数禁止隐式类型转换标准库中类含有单参数的构造函数 接收一个单参数的const char* 的string构造函数不是explicit接收一个容量参数的vector的构造函数是explicit 匿名对象 匿名对象的生命周期在当前行匿名对象具有常性const引用会延长匿名对象的生命周期生命周期在引用对象的当前函数作用域 同一行一个表达式中连续的构造拷贝构造一般编译器会优化合二为一 隐式类型转换传参连续构造拷贝构造——》优化为直接构造匿名对象的传参连续构造拷贝构造——》优化为一个构造接收非引用的自定义类型连续拷贝构造拷贝构造——》优化为一个拷贝构造注意一个表达式中连续拷贝构造赋值重载——》无法优化建议在传参和接收非引用返回值等场景使用连续构造因为编译器会优化