STL用法整理
百度百科
stl是standard template library的简称,中文名标准模板库,惠普实验室开发的一系列软件的统称。从根本上说,stl是一些“容器”的集合,这些“容器”有list,vector,set,map等,stl也是算法和其他一些组件的集合。stl的目的是标准化组件,这样就不用重新开发,可以使用现成的组件。stl现在是c++的一部分,因此不用安装额外的库文件。
在c++标准中,stl被组织为下面的13个头文件:<algorithm>、<deque>、<functional>、<iterator>、<array>、<vector>、<list>、<forward_list>、<map>、<unordered_map>、<memory>、<numeric>、<queue>、<set>、<unordered_set>、<stack>和<utility>。
向量(vector) 连续存储的元素<vector>
列表(list) 由节点组成的双向链表,每个结点包含着一个元素<list>
双队列(deque) 连续存储的指向不同元素的指针所组成的数组<deque>
集合(set) 由节点组成的红黑树,每个节点都包含着一个元素,节点之间以某种作用于元素对的谓词排列,没有两个不同的元素能够拥有相同的次序 <set>
多重集合(multiset) 允许存在两个次序相等的元素的集合 <set>
栈(stack) 后进先出的值的排列 <stack>
队列(queue) 先进先出的执的排列 <queue>
优先队列(priority_queue) 元素的次序是由作用于所存储的值对上的某种谓词决定的的一种队列 <queue>
映射(map) 由{键,值}对组成的集合,以某种作用于键对上的谓词排列 <map>
多重映射(multimap) 允许键对有相等的次序的映射 <map>
一、string
在定义 string 类对象时,string 类自身可以管理内存,程序员不必关注内存的分配细节。
string 类提供的各种操作函数大致分为八类:构造器和析构器、大小和容量、元素存取、字 符串比较、字符串修改、字符串接合、i/o 操作以及搜索和查找。
要使用 string 类,必须包含头文件 <string>
string stuff; getline(cin, stuff); //获取一行字符串
1、构造函数
string strs //生成空字符串 string s(str) //生成字符串str的复制品 string s(str, stridx) //将字符串str中始于stridx的部分作为构造函数的初值 string s(str, strbegin, strlen) //将字符串str中始于strbegin、长度为strlen的部分作为字符串初值 string s(cstr) //以c_string类型cstr作为字符串s的初值 string s(cstr,char_len) //以c_string类型cstr的前char_len个字符串作为字符串s的初值 string s(num, c) //生成一个字符串,包含num个c字符 string s(strs, beg, end) //以区间[beg, end]内的字符作为字符串s的初值
string s('x'); //错误 string s(1, 'x'); //正确 string s("x"); //正确
2、获取字符串长度
str.size(); //返回 string 类型对象中的字符个数 str.length(); //返回 string 类型对象中的字符个数 str.max_size(); //返回 string 类型对象最多包含的字符数。一旦程序使用长度超过 max_size() 的 string 操作,编译器会拋出 length_error 异常。 str.capacity(); //返回在重新分配内存之前string 类型对象所能包含的最大字符数。
3、获取字符串元素:
一般可使用两种方法访问字符串中的单一字符:下标操作符[] 和 成员函数at()。两者均返回指定的下标位置的字符。第 1 个字符索引(下标)为 0,最后的字符索引为 length()-1。
需要注意的是,这两种访问方法是有区别的:
- 下标操作符 [] 在使用时不检查索引的有效性,如果下标超出字符的长度范围,会示导致未定义行为。对于常量字符串,使用下标操作符时,字符串的最后字符(即 '\0')是有效的。对应 string 类型对象(常量型)最后一个字符的下标是有效的,调用返回字符 '\0'。
- 函数 at() 在使用时会检查下标是否有效。如果给定的下标超出字符的长度范围,系统会抛出 out_of_range 异常。
string s="abcd"; temp = str [2]; //获取字符 'c' temp_1 = str.at(2); //获取字符 'c'
4、字符串比较方法
basic_string 类模板既提供了 >、<、==、>=、<=、!= 等比较运算符,还提供了 compare() 函数,其中 compare() 函数支持多参数处理,支持用索引值和长度定位子串进行比较。该函数返回一个整数来表示比较结果。如果相比较的两个子串相同,compare() 函数返回 0,否则返回非零值。
string a ("abcdef"); string b ("abcdef"); string c ("123456"); string d ("123dfg"); //下面是各种比较方法 int m=a.compare (b); //完整的a和b的比较 32 int n=a.compare(1,5,b,1,5); //a的"bcdef"和b的"bcdef"比较 -32 int q=c.compare(0,3,d,0,3); //"123"和"123"比较 0
5、字符串内容的修改
可以通过使用多个函数修改字符串的值。例如 assign(),operator=,erase(),交换(swap),插入(insert)等。另外,还可通过 append() 函数添加字符。
str.assign(str1); //str1赋值给str str.assign (str1 , 3, 3); //将从str1[3]长度3的子串赋值给str str.assign (str1, 2, str1.npos); //将从str1[2]到末尾赋值给str str.assign (5, 'x'); //将xxxxx赋值给str string::iterator itb; //迭代器itb itb = str1.begin (); //将str1.begin(),即1赋值给迭代器itb str.erase(3); //从尾开始,删除3个值 str.swap(str2); //str值与str2值交换 b.insert (1, a); //在b[1]位置插入a b.insert (1, "yanchy ", 3); //将“yanchy”长度为3的子串插入b[1] b.insert(1,a,2,2); //将a从索引2,长度为2的子串插入b[1] b.insert (1,5,'c'); b.append (a); //将a追加到b后 b.append("12345", 2); //将从索引0,长度为2的子串追加至b b.append ("12345", 2, 3); //将从索引2,长度为3的子串追加至b b.append (10 , 'a'); //追加10个a
6、字符串内容的替换
如果在一个字符串中标识出具体位置,便可以通过下标操作修改指定位置字符的值,或者替换某个子串。完成此项操作需要使用 string 类的成员函数 replace()。
var.replace (3,3, “1234”); var.replace (3, 1, 5, 'x'); var.replace (3,1, var1.c_str(), 1, 3);
7、字符串查找函数
str.find ('p', 5); str.find (" some", 0);
二、序列容器
1、array
array<t,n> 模板定义了一种相当于标准数组的容器类型。它是一个有 n 个 t 类型元素的固定序列。除了需要指定元素的类型和个数之外,它和常规数组没有太大的差别。显然,不能增加或删除元素。
1.1初始化
array<double, 100> data; //定义100个double型元素,但未初始化 array<double, 100> data {}; //初始化 array<double, 10> values {0.5, 1.0}; //创建 array 容器的实例时,对元素进行初始化 values.fill(3.14); //将所有元素设成给定值
1.2获取元素
values[4] = values[3] + 2.o*values[1]; //索引 values.at (4) = values.at(3) + 2.o*values.at(1); //at() auto first = height_ins.begin(); //begin() auto last = height_ins.end () ; //end()
2、vector
vector<t> 容器是包含 t 类型元素的序列容器,和 array<t,n> 容器相似,不同的是 vector<t> 容器的大小可以自动增长,从而可以包含任意数量的元素;因此类型参数 t 不再需要模板参数 n。只要元素个数超出 vector 当前容量,就会自动分配更多的空间。只能在容器尾部高效地删除或添加元素。
2.1初始化
vector<int> v {2, 3, 5, 7, 11}; //以初始化列表中的値作为元素初始值,生成有 5 个int型 vector 容器 vector<double> values(20); //容器开始时有 20 个元素,它们的默认初始值都为 0 vector<char >v (10, 'a'); //容器开始时有 10 个元素,它们的默认初始值都为 char型 a
array<string> words {"one", "two","three", "four", "five"}; vector<string> words_copy {begin(words) , end(words)}; //元素复制
vector<string〉words_copy {make_move_iterator(begin(words)),make_move_iterator(end(words))}; //元素移动
struct node{
//初始化结构体 //省略代码 }; vector<node*>v;
2.2大小与容量
size()、capacity()
vector<size_t> v { 2, 3, 5, 7, 11, 13, 17, 19, 23 }; //初始化 cout << "the size is " << v.size() << std::endl; //v.size() 计算元素个数 cout << "the capacity is" << v.capacity() << std::endl; //v.capacity() 计算容量大小
2.3获取元素
front()、back()、begin()、end()、索引
通过使用索引,总是可以访问到现有的元素或为现有元素设定值,但是不能这样生成新元素。
vector<double> v(20); v[0] = 3.14159; v[1] = 5.0; v[2] = 2.0*[0]*v[1];
cout << v.front () << endl; //front()返回序列中第一个元素的引用 cout << v.front () << endl; //back()返回序列中最后一个元素的引用 v.front() = 2.71828; //因为返回的是引用,所以可以出现在赋值运算符的左边。 auto pdata = v.data(); //返回一个指向数组的指针,它在内部被用来存储元素。data() 返回 vector<t> 容器的 t* 类型的值。
2.4添加元素
push_back()、emplace_back()、insert()
emplace_back() 函数会调用接收三个参数的 string 构造函数,生成 string 对象,然后把它添加到 words 序列中。构造函数会生成一个从索引 2 幵始、包含 str 中三个字符的子串。
vector<int> v; v.push_back(3); //push_back()把3添加到现有元素的后面 string str {"alleged"}; words.emplace_back(str, 2, 3); // "leg" in place
insert()
1) 插入第二个参数指定的单个元素
vector<string> words { "one","three","eight"} //初始化 auto iter = words.insert(++begin(words), "two"); //one two three eight 返回的迭代器指向被插入的元素 string(”two”)
2) 插入一个由第二个和第三个参数指定的元素序列
string more[] {"five", "six", "seven" }; //初始化 auto iter = words.insert(--end(words) ,begin(more), end(more)); //one two three five six seven eight 返回的迭代器指向插入的第一个元素"five"。
3) 在插入点插入多个单个元素。第二个参数是第三个参数所指定对象的插入次数
vector<int> words{ 1,2,4 }; //初始化 auto iter = words.insert(--end(words), 2, 3); //1 2 3 3 4
2.5删除元素
clear()、pop_back()、erase()
v.clear(); //删除所有元素 v.pop_back(); //删除尾部元素 v.erase(begin(v) + 1); //删除第二个元素 vector<int> v{ 1,2,3,4,5 }; //删除范围内的元素,左闭右开 v.erase(begin(v)+1,begin(v)+3); //1,4,5
2.6其他
v.empty() //判空 v1.swap(v2) //交换容器间数据 reverse(v.begin(), v.end()); //反转
3、deque
deque<t>,一个定义在 deque 头文件中的容器模板,可以生成包含 t 类型元素的容器,它以双端队列的形式组织元素。可以在容器的头部和尾部高效地添加或删除对象,这是它相对于 vector 容器的优势。
3.1初始化
deque<int> d; //生成deque容器,容器中没有元素,因此添加第一个元素,就会导致内存的分配 deque<int> d(10); //10个元素,默认值为0 deque<string> words { "one", "none", "some", "all" }; //初始化 deque<string> words_copy { words }; //生成words的副本 deque<string> words_part { begin(words),begin(words) + 5 }; //迭代器标识范围初始化
3.2获取元素
索引、at()、front()、back()
3.3大小
size(); 因为deque容量和大小总是一样,所以并没有capacity()
3.4添加和删除元素
与vector一样都有成员函数:push_back()、pop_back()、emplace_back()、insert()、erase()、clear()
deque还有:push_front()、pop_front()、emplace_front()
3.5修改替换元素
assign()
4、list
list<t> 容器模板定义在 list 头文件中,是 t 类型对象的双向链表。
4.1初始化
list<string> l; list<string> l {20}; //二十个元素 list<double> l (50, 3.14); //50个3.14 list<double> l {l1}; //生成l1(list容器)的副本 list<double> l {++cbegin(l1), --cend(l1)}; //l1的开始和结束迭代器所指定的一段元素
4.2大小
size()
4.3添加删除元素
添加:
push_back()、push_front()、emplace_back()、emplace_front(),后两个函数更高效。
insert(),插入元素不必移动现有的元素。生成新元素后,这个过程需要将 4 个指针重新设为适当的值。第一个元素的 next 指针指向新的元素,原来的第二个元素的 pre 指针也指向新的元素。新元素的 pre 指针指向第一个元素,next 指针指向序列之前的第二个元素。
删除:
clear()、erase(),list 容器的成员函数 remove() 则移除和参数匹配的元素。
成员函数 remove_if() 期望传入一个一元断言作为参数。一元断言接受一个和元素同类型的参数或引用,返回一个布尔值。断言返回 true 的所有元素都会被移除。
numbers.remove_if([](int n){return n%2 == 0;}); // remove even numbers. result 5 3 7 9
成员函数 unique()可以移除连续的重复元素,只留下其中的第一个。
list<string> words { "one", "two", "two", "two","three", "four", "four"}; words.unique () ; // now contains "one" "two" "three" "four"
4.4排序与合并元素
list 容器并不提供随机访问迭代器,只提供双向迭代器,因此不能对 list 中的元素使用 sort() 算法,而是定义了自己的 sort() 函数。
names.sort(greater<>()); //从大到小排序,头文件 functional 中的模板 greater<t> names.sort(cmp()); //或者自定义比较函数
list 的成员函数 merge() 以另一个具有相同类型元素的 list 容器作为参数。两个容器中的元素都必须是升序。参数 list 容器中的元素会被合并到当前的 list 容器中。
list<int> my{2, 4, 6, 14}; list<int> your{ -2, 1, 7, 10}; my.merge (your); //my: -2 1 2 4 6 7 10 14 your.empty(); //your变为空
list 容器的成员函数 splice() 有几个重载版本。这个函数将参数 list 容器中的元素移动到当前容器中指定位置的前面。可以移动单个元素、一段元素或源容器的全部元素。
三、容器适配器
1、stack
stack<t>:是一个封装了 deque<t> 容器的适配器类模板,默认实现的是一个后入先出(last-in-first-out,lifo)的压入栈。stack<t> 模板定义在头文件 stack 中。
- top():返回一个栈顶元素的引用,类型为 t&。如果栈为空,返回值未定义。
- push():可以将对象副本压入栈顶。这是通过调用底层容器的 push_back() 函数完成的。
- pop():弹出栈顶元素。
- size():返回栈中元素的个数。
- empty():在栈中没有元素的情况下返回 true。
- swap(stack<t> & other_stack):将当前栈中的元素和参数中的元素交换。参数所包含元素的类型必须和当前栈的相同
2、queue
queue<t>:是一个封装了 deque<t> 容器的适配器类模板,默认实现的是一个先入先出(first-in-first-out,lifo)的队列。queue<t> 模板定义在头文件 queue 中。
- front():返回 queue 中第一个元素的引用。如果 queue 是常量,就返回一个常引用;如果 queue 为空,返回值是未定义的。
- back():返回 queue 中最后一个元素的引用。如果 queue 是常量,就返回一个常引用;如果 queue 为空,返回值是未定义的。
- push():在 queue 的尾部添加一个元素的副本。这是通过调用底层容器的成员函数 push_back() 来完成的。
- pop():删除 queue 中的第一个元素。
- size():返回 queue 中元素的个数。
- empty():如果 queue 中没有元素的话,返回 true。
- swap(queue<t> &other_q):将当前 queue 中的元素和参数 queue 中的元素交换。它们需要包含相同类型的元素。也可以调用全局函数模板 swap() 来完成同样的操作。
priority_queue<t>:是一个封装了 vector<t> 容器的适配器类模板,默认实现的是一个会对元素排序,从而保证最大元素总在队列最前面的队列。 priority_queue<t> 模板定义在头文件 queue 中。
- push():将obj放到容器的适当位置,这通常会包含一个排序操作。
- top():返回优先级队列中第一个元素的引用。
- pop():移除第一个元素。
- size():返回队列中元素的个数。
- empty():如果队列为空的话,返回true。
- swap(priority_queue<t>& other):和参数的元素进行交换,所包含对象的类型必须相同
四、map容器
map 容器是关联容器的一种。在关联容器中,对象的位置取决于和它关联的键的值。
map 和 mutilmap 容器的模板定义在 map 头文件中,unordered_map 和 unordered_multimap 容器的模板定义在 unordered_map 头文件中。
map 容器有 4 种,每一种都是由类模板定义的。每种 map 容器的模板都有不同的特性:
- map<k,t>容器,保存的是 pair<const k,t> 类型的元素。键唯一;map 容器中的元素都是有序的,元素在容器内的顺序是通过比较键确定的。
- multimap<k,t> 容器,也会对元素排序。元素的顺序是通过比较键确定的。但multimap 容器可以保存多个具有相同键值的 <const k,t> 元素。
- unordered_map<k,t> 中 pair< const k,t>元素的顺序是由键值的哈希值决定的。不允许有重复的键。
- unordered_multimap<k,t> 通过键值生成的哈希值来确定对象的位置,但允许有重复的键。
区别: multi前缀表明键不必唯一
unordered前缀表明容器中元素的位置是通过其键值所产生的哈希值来决定的,而不是通过比较键值决定的。