什么是STL:
标准模板库(Standard Template Library,简称STL)简单说,就是一些常用数据结构和算法的模板的集合。
容器可以用于存放各种类型的数据(基本类型的变量,对象等)的数据结构,都是模板类,分为顺序容器、关联式容器、容器适配器三种类型。
三种容器类型特性分别如下:
容器并非排序的,元素的插入位置同元素的值无关。包含vector、deque、list。
(1)vector 头文件
动态数组。元素在内存连续存放。随机存取任何元素都能在常数时间完成。在尾端增删元素具有较佳的性能。
(2)deque 头文件
双向队列。元素在内存连续存放。随机存取任何元素都能在常数时间完成(仅次于vector)。在两端增删元素具有较佳的性能(大部分情况下是常数时间)。
(3)list 头文件
双向链表。元素在内存不连续存放。在任何位置增删元素都能在常数时间完成。不支持随机存取。
元素是排序的;插入任何元素,都按相应的排序规则来确定其位置;在查找时具有非常好的性能;通常以平衡二叉树的方式实现。包含set、multiset、map、multimap。
(1)set/multiset 头文件
set 即集合。set中不允许相同元素,multiset中允许存在相同元素。
(2)map/multimap 头文件
map与set的不同在于map中存放的元素有且仅有两个成员变,一个名为first,另一个名为second, map根据first值对元素从小到大排序,并可快速地根据first来检索元素。 键–值。
注意:map同multimap的不同在于是否允许相同first值的元素。
封装了一些基本的容器,使之具备了新的函数功能,比如把deque封装一下变为一个具有stack功能的数据结构。这新得到的数据结构就叫适配器。包含stack,queue,priority_queue。
(1)stack 头文件
栈是项的有限序列,并满足序列中被删除、检索和修改的项只能是最进插入序列的项(栈顶的项)。后进先出。
(2)queue 头文件 队列。
插入只可以在尾部进行,删除、检索和修改只允许从头部进行。先进先出。
(3)priority_queue 头文件
优先级队列。内部维持某种有序,然后确保优先级最高的元素总是位于头部。最高优先级元素总是第一个出列。
一般情况下,一个程序包括数据结构和相应的算法,而数据结构作为存储数据的组织形式,与内存空间有着密切的联系。在C++ STL中,空间配置器便是用来实现内存空间(一般是内存,也可以是硬盘等空间)分配的工具,与容器联系紧密,每一种容器的空间分配都是通过空间分配器alloctor实现的。
从内存空间分配的角度来对这两种方式的区别,就比较容易区分:
(1)对于第一种方式来说,是直接通过调用Test类的构造函数来实例化Test类对象的,如果该实例化对象是一个局部变量,则其是在栈空间分配相应的存储空间。
(2)对于第二种方式来说,就显得比较复杂。这里主要以new类对象来说明一下。new一个类对象,其实是执行了两步操作:首先,调用new在堆空间分配内存,然后调用类的构造函数构造对象的内容;同样,使用delete释放时,也是经历了两个步骤:首先调用类的析构函数释放类对象,然后调用delete释放堆空间。
STL中常用的容器有vector、deque、list、map、set、multimap、multiset、unordered_map、unordered_set、stack、queue和priority_queue。
STL中迭代器的作用,有指针为何还要迭代器?
Iterator类的访问方式就是把不同集合类的访问逻辑抽象出来,使得不用暴露集合内部的结构而达到循环遍历集合的效果。
迭代器的作用:
(1)用于指向顺序容器和关联容器中的元素
(2)通过迭代器可以读取它指向的元素
(3)通过非const迭代器还可以修改其指向的元素
迭代器用于提供一种方法顺序访问一个聚合对象中各个元素, 而又不需暴露该对象的内部表示。
#include <iostream> #include <vector> using namespace std; int main() { vector<int> v; //一个存放int元素的数组,一开始里面没有元素 v.push_back(1); v.push_back(2); v.push_back(3); v.push_back(4); vector<int>::const_iterator i; //常量迭代器 for (i = v.begin(); i != v.end(); ++i) //v.begin()表示v第一个元素迭代器指针,++i指向下一个元素 cout << *i << ","; //*i表示迭代器指向的元素 cout << endl; vector<int>::reverse_iterator r; //反向迭代器 for (r = v.rbegin(); r != v.rend(); r++) cout << *r << ","; cout << endl; vector<int>::iterator j; //非常量迭代器 for (j = v.begin();j != v.end();j++) *j = 100; for (i = v.begin();i != v.end();i++) cout << *i << ","; return 0; } /* 运行结果: 1,2,3,4, 4,3,2,1, 100,100,100,100, */
首先两个概念:
(1)capacity:该值在容器初始化时赋值,指的是容器能够容纳的最大的元素的个数。还不能通过下标等访问,因为此时容器中还没有创建任何对象。
(2)size:指的是此时容器中实际的元素个数。可以通过下标访问0-(size-1)范围内的对象。
resize和reserve区别主要有以下几点:
resize和reserve的区别:
(1)resize既分配了空间,也创建了对象;reserve表示容器预留空间,但并不是真正的创建对象,需要通过insert()或push_back()等创建对象。
(2)resize既修改capacity大小,也修改size大小;reserve只修改capacity大小,不修改size大小。
(3)两者的形参个数不一样。 resize带两个参数,一个表示容器大小,一个表示初始值(默认为0);reserve只带一个参数,表示容器预留的大小。
#include<iostream> #include<vector> using namespace std; int main() { vector<int> a; cout<<"initial capacity:"<<a.capacity()<<endl; cout<<"initial size:"<<a.size()<<endl; /*resize改变capacity和size*/ a.resize(20); cout<<"resize capacity:"<<a.capacity()<<endl; cout<<"resize size:"<<a.size()<<endl; vector<int> b; /*reserve改变capacity,不改变resize*/ b.reserve(100); cout<<"reserve capacity:"<<b.capacity()<<endl; cout<<"reserve size:"<<b.size()<<endl; return 0; } /* 运行结果: initial capacity:0 initial size:0 resize capacity:20 resize size:20 reserve capacity:100 reserve size:0 */
map和unordered_map的区别在于他们的实现基理不同。
vector和list区别在于底层实现机理不同,因而特性和适用场景也有所不同。
//新增元素 void insert(const_iterator iter,const T& t ) { int index=iter->begin(); if (index<size_) { if (size_==capacity_) { int capa=calculateCapacity(); newCapacity(capa); } memmove(buf+index+1,buf+index,(size_-index)*sizeof(T)); buf[index]=t; size_++; } }
删除元素
删除最后一个元素pop_back()和通过迭代器删除任意一个元素erase(iter)。
迭代器iteraotr
迭代器iteraotr是STL的一个重要组成部分,通过iterator可以很方便的存储集合中的元素.STL为每个集合都写了一个迭代器, 迭代器其实是对一个指针的包装,实现一些常用的方法,如++,–,!=,==,*,->等, 通过这些方法可以找到当前元素或是别的元素。
map是关联式容器,它们的底层容器都是红黑树。map 的所有元素都是 pair,同时拥有实值(value)和键值(key)。pair 的第一元素被视为键值,第二元素被视为实值。所有元素都会根据元素的键值自动被排序。不允许键值重复。
如果要将一个临时变量push到容器的末尾,push_back()需要先构造临时对象,再将这个对象拷贝到容器的末尾,而emplace_back()则直接在容器的末尾构造对象,这样就省去了拷贝的过程。
#include <iostream> #include <cstring> #include <vector> using namespace std; class A { public: A(int i){ str = to_string(i); cout << "构造函数" << endl; } ~A(){} A(const A& other): str(other.str){ cout << "拷贝构造" << endl; } public: string str; }; int main() { vector<A> vec; vec.reserve(10); for(int i=0;i<10;i++){ vec.push_back(A(i)); //调用了10次构造函数和10次拷贝构造函数, // vec.emplace_back(i); //调用了10次构造函数,一次拷贝构造函数都没有调用过 }