1.STL
[1]STL容器是直接管理存储数据。
[2]STL适配器是在这些容器上面增加一些操作函数,封装成一个模板。
2.STL 容器
[1]容器类型:
1)array:数组。
2)bitset:二进制位操作。
3)deque:双向队列。
4)forward_list:单向链表。
5)list:双向链表。
6)map(multimap):map:键值非重复容器,multimap:键值可重复容器。
7)set(multiset):自动排序非重复集合。
8)vector:动态数组。
9)unordered_set(unordered_multiset):按照hash来存储的set(multiset)。
10)unordered_map(unordered_multimap):按照hash来存储的map(multiset)。
[2]容器的分类:
1)容器分为顺序容器和关联容器。
2)顺序容器是不进行自动排序的,关联容器是按照元素的值进行自动排序的。其中map和set是关联容器,其他的是顺序容器,最主要的就是关联容器有find方法,可以快速查找元素。
[3]顺序容器的使用说明:
1)除了array是固定大小外,其他容器都提供自动内存管理。
2)string和vector保存在连续内存中,因此随机访问很快,但是在中间插入删除会很慢。
3)deque随机访问很快,并且在两端插入删除很快,但是在中间插入删除会很慢。
4)list和forward_list在任何地方插入删除很快,随机访问很慢。
[4]关联容器的使用说明:
1)map的使用,map是一个键值对,其中值是一个pair对象:
#include <stdio.h> #include <stdlib.h> #include <string> #include <vector> #include <iostream> #include <algorithm> #include <set> #include <map> #include <arpa/inet.h> using namespace std; class ca { public: int ip; }; class comp{ public: bool operator()(const ca &a, const ca &b) { return a.ip < b.ip; } }; bool operator<(const ca &a, const ca &b) { return a.ip < b.ip; } int main() { //map的第三个参数,另外第三个参数使用普通函数时编译不过不知怎么回事 ca a1, a2, a3; map<ca, string> addr2; //使用全局的<运算符重载函数 map<ca, string, comp> addr1; //使用仿函数 addr1.insert(pair<ca, string>(a1, "1 addr a1")); addr2.insert(pair<ca, string>(a1, "2 addr a1")); //pair的使用
pair<ca, string> p1(a1, "1 addr a1"); //创建一个pair对象 addr1.insert(p1);
addr1.insert(pair<ca, string>(a2, "1 addr a2")); //隐式的创建一个pair对象,这个是常用的方式,因为通常没有一个现成的对象 addr1.insert(make_pair(a2, "1 addr a3")); //调用make_pair创建一个pair对象 //关联容器定义的类型 /* map的元素类型是一个pair对象,这个很重要。 元素: map的元素就是map容器里面的一个值。就像是set<int>的每个元素是一个int型对象。 关键字: map的关键字是pair对象的第一个元素。 */ map<ca, string>::key_type v1; //v1的类型是类ca,代表关键字的类型 map<ca, string>::mapped_type v2; //v2的类型是string,代表关键字关联的类型 map<ca, string>::value_type v3; //v3的类型是pair<ca, string>,代表值的类型 set<string>::key_type v4; //v4和v5的类型是string,set的键值类型是一样的 set<string>::value_type v5; return 0; }
2)关键字类型必须定义元素的比较方法,这个是肯定的,否则无法进行排序。实际编程中,如果一个类定义了<运算符,就可以作为关键字了,这个意思就是说关键字没有必要实现>和=运算符,实现了和不实现效果是一样的。还有就是定义<运算符要符合严格弱序的规则,这意思就是自定义的<运算符函数,实现要合理,例如传给这函数一个(a,b),返回true,传给一个(b,a),还是返回true,那就不符合严格弱序了。虽然不符合但是不会导致编译错误,只是用的时候会发生各种错误,例如set插入了相同的元素。
严格弱序规则就是:(1)如果传入(a,b)返回true,则传入(b,a)必须返回false。
(2)如果传入(a,b)返回true,传入(c,a)返回true,则传入(c,b)必须返回true。就是说a<b,c<a,则c要小于b。
#include <stdio.h> #include <stdlib.h> #include <string> #include <vector> #include <iostream> #include <algorithm> #include <set> #include <arpa/inet.h> using namespace std; class ca { public: unsigned ip; int port; ca(int ip1, int port1) {ip = ip1; port = port1;} //怎么定义<运算符都是可以的,看需求。 //只是定义不同时,元素的排序顺序是不一样的。 //如果用这种方式,则set里面的排列顺序为: // 1.1.1.1:80 // 1.1.1.1:81 // 1.1.1.2:80 // 1.1.1.2:81 // //这意思就是用ip去比较地址,如果ip不同时再用port去比较地址,当然使用这种需求一般用的比较多,这种方让元素之间有一层优先级。 bool operator<(const ca &other) const{ if(ip < other.ip) return true; else if(other.ip < ip) return false; if(port < other.port) return true; else if(other.port < port) return false; return false; } //完全等同于上面的函数 bool operator<(const ca &other) const{ if(ip < other.ip ||(ip == other.ip && port <other.port)) return true; return false; } //如果用这种方式,则set里面的排列顺序为: // 1.1.1.1:80 // 1.1.1.2:80 // 1.1.1.1:81 // 1.1.1.2:81 //这意思就是说希望用ip或port去比较地址,只要有一个小于就是小于,很明显这种方式用的少。 bool operator<(const ca &other) const { if(ip < other.ip || port < other.port) return true; return false; } //错误的方式,错误的函数不会导致编译错误,但是处理的时候会发生错误。例如下面这种就可以把相同的数据插入到set里面。 //很明显已经违背了严格弱序的原则,因为a < b为true的时候同时b < a也为true。 bool operator<(const ca &other) const{ return true; } }; int main() { set<ca> v; set<ca>::iterator it; v.insert(ca(inet_addr("1.1.1.1"), 80)); v.insert(ca(inet_addr("1.1.1.1"), 81)); v.insert(ca(inet_addr("1.1.1.2"), 80)); v.insert(ca(inet_addr("1.1.1.2"), 81)); for(it=v.begin(); it!=v.end(); it++) printf("%s:%d\n", inet_ntoa(*((struct in_addr *)&(it->ip))), it->port); return 0; }
3)set的迭代器是const的,就是说不能通过一个set迭代器去修改一个值,这个也是当然的,因为set是有序的,修改了就无序了,同理map的key值也是不能通过迭代器修改的,但是map的value是可以通过迭代器修改的。
4)map和set的insert操作可以检查返回值,判断是否插入成功,例如元素已经存在则insert会失败,返回的pair类型的第二个值为false。
5)lower_bound和upper_bound:这两个操作都返回一个迭代器,如果关键字在容器中,lower_bound返回的迭代器指向第一个元素,upper_bound返回的迭代器指向最后一个位置的之后;如果关键字不在容器中,则lower_bound和upper_bound返回相同的位置,这个位置就是不影响排序的关键字的插入位置。如果存在则只要(begin=lower_bound;begin!=upper_bound;begin++;)就可以遍历这个关键字对应的所有元素。如果不存在,则返回的迭代器指向的位置是要查的关键字可以插入的位置。
6)multimap的使用(想必multiset的用法也是差不多的)例:
#include <stdio.h> #include <stdlib.h> #include <string> #include <vector> #include <iostream> #include <algorithm> #include <set> #include <map> #include <arpa/inet.h> using namespace std; /* 查找mulimap关键字的方法 */ int main() { // 1.使用lower_bound和upper_bound multimap<int, string> m; multimap<int, string>::iterator begin, end; m.insert(pair<int, string>(1, "boy1")); m.insert(pair<int, string>(2, "girl1")); m.insert(pair<int, string>(2, "girl2")); m.insert(pair<int, string>(2, "girl3")); m.insert(pair<int, string>(5, "dog1")); m.insert(pair<int, string>(5, "dog2")); /* 输出: key 2 value:girl1 key 2 value:girl2 key 2 value:girl3 end value:dog1 可见begin指向(2, "girl1"),end指向(5, "dog1")。 */ begin = m.lower_bound(2); end = m.upper_bound(2); while(begin != end){ printf("key 2 value:%s\n", begin->second.c_str()); begin++; } printf("end value:%s\n", end->second.c_str()); /* 输出: begin value:dog1 end value:dog1 可见begin 和end都指向(5, "dog1"),因为此位置就是要查的关键字3应该插入的位置。 */ begin = m.lower_bound(3); end = m.upper_bound(3); printf("begin value:%s\n", begin->second.c_str()); printf("end value:%s\n", end->second.c_str()); /* 输出: begin value:0x7fff4e76b578 end value :0x7fff4e76b578 m.end value:0x7fff4e76b578 可见begin 和end都指向m.end(),因为此位置就是要查的关键字10应该插入的位置。 */ begin = m.lower_bound(10); end = m.upper_bound(10); printf("begin value:%p\n", begin); printf("end value:%p\n", end); printf("m.end value:%p\n", m.end()); // 2.使用count方法得到个数 int count; multimap<int, string>::iterator it; count = m.count(5); //得到关键字5的元素个数 it = m.find(5); //指向关键字5的第一个元素 /* 输出: key 5 value:dog1 key 5 value:dog2 */ while(count > 0){ printf("key 5 value:%s\n", it->second.c_str()); it++; count--; } // 3.使用equal_range pair<multimap<int, string>::iterator, multimap<int, string>::iterator> range; range = m.equal_range(2); //直接得到关键字2的迭代器的起始位置 /* 输出: key 2 value:girl1 key 2 value:girl2 key 2 value:girl3 */ while(range.first != range.second){ printf("key 2 value:%s\n", range.first->second.c_str()); range.first++; } return 0; }
7)unordered_map和unordered_set:这两个容器是无序的,就是说不是按顺序存储的。因为map和set都是自动排序的,例如遍历map或set,可以得到按顺序排序的值。但是unordered_map和unordered_set是按hash存储的,遍历会得到不按顺序输出的值,所以如果要求存储的元素有序就用map,如果无序并且需要快速查找,就用unordered_map。map和unordered_map效率比较的例子:
#include <stdio.h> #include <stdlib.h> #include <sys/time.h> #include <string> #include <vector> #include <iostream> #include <algorithm> #include <set> #include <map> #include <arpa/inet.h> #include <tr1/unordered_map> using namespace std::tr1; using namespace std; double timeuse(struct timeval begin, struct timeval end) { double res; if(end.tv_usec < begin.tv_usec){ end.tv_usec += 1000000; end.tv_sec--; } res = end.tv_usec - begin.tv_usec; res += (end.tv_sec - begin.tv_sec)*1000000; return res; } int main() { int i; map<int, int> m; map<int, int>::iterator it; unordered_map<int, int> um; unordered_map<int, int>::iterator uit; struct timeval tv_begin, tv_end; double res; #define BEGIN gettimeofday(&tv_begin, NULL); #define END(s) do{\ gettimeofday(&tv_end, NULL); \ res = timeuse(tv_begin, tv_end); \ printf("%-30s, usetime:%f usec [%f sec]\n", s, res, res/1000000);\ }while(0) BEGIN for(i=0; i<10000000; i++) m.insert(pair<int, int>(i, i)); END("map insert"); BEGIN for(i=0; i<10000000; i++) um.insert(pair<int, int>(i, i)); END("unordered_map insert"); BEGIN it = m.find(500000); END("map find"); BEGIN uit = um.find(500000); END("unordered_map find"); return 0; }
输出:
map insert , usetime:10102899.000000 usec [10.102899 sec] unordered_map insert , usetime:2749505.000000 usec [2.749505 sec] map find , usetime:3.000000 usec [0.000003 sec] unordered_map find , usetime:1.000000 usec [0.000001 sec]
由此可见unordered_map在插入和查找方面还是比map要快很多的,当然内存占用的也多。
3.STL适配器
[1]stack:栈适配器。这个要注意的就是栈里面是有一个容器来存储元素的,常规的理解栈应该就是一个数组,其实不是,栈默认的容器是一个双向队列(deque),栈也可以用vector、list等作为元素的容器,有个条件就是容易需要提供push_back()、back()、和pop_back()方法,因为set没有提供这些方法,所以不能把set作为栈的容器。所谓栈就是在一个容器上面加了一些操作函数,封装成了栈模板(STL中栈定义:template < class T, class Container = deque<T> > class stack;)。
[2]queue:单向队列适配器。这个和stack是一样的,特点是先进先出。当然也是需要提供一个容器来存储元素,默认是deque。
[3]priority_queue :优先级队列适配器。这个适配器是把容器内的元素排序好的,每次可以获取优先级最高的元素。所以可以传入一个优先级比较函数,默认比较函数是<,默认的容器是vector,定义方式priority_queue<Type, Container, Functional>。其实这个适配器只是在queue上面加了一个优先级排序函数。
4.STL具体用法
[1]const_iterator:如果不需要写访问容器时,应该使用const_iterator迭代器,方法和iterator是一样的。
[2]reverse_iterator(const_reverse_iterator):可以从后向前遍历容器。
[3]管理容量:除了array之外,容器的内存都是自动管理的。size()表示当前的元素数量,capacity()表示已经预分配的容量,reserve()表示设置预分配的容量。这个意思就是说capacity()是已经预分配好的容量,不管是程序自动分配还是手动调用reserve()分配。还有就是resize()是设置容器使用的大小,和预分配没有关系,例如当前元素个数是5,则resize(8)会往容器增加3个元素,resize(4)会删除1个元素。
[4]substr:拷贝原始string的一部分或全部。另外string类还提供了一些其他方法,来完成和char *类型的字符串的处理函数,例如类似strstr、strrstr的函数。
举例:
#include <stdio.h> #include <stdlib.h> #include <string> #include <vector> #include <iostream> #include <algorithm> #include <array> using namespace std; int main() { vector<int> v; vector<int>::const_iterator c_it; vector<int>::const_reverse_iterator cr_it; v.push_back(1); v.push_back(2); v.push_back(3);v.push_back(4);v.push_back(5); //iterator对应的是begin、end for(c_it = v.begin(); c_it!= v.end(); c_it++) printf("%d\n", *c_it); //reverse_iterator对应的是rbegin、rend,cr_it = v.begin()是编译不过的 for(cr_it = v.rbegin(); cr_it!= v.rend(); cr_it++) printf("%d\n", *cr_it); // 容量设置 printf("size: %u\n", v.size()); printf("capacity:%u\n", v.capacity()); //程序自动预分配的容量,例如值是8 printf("--------------------------\n"); v.reserve(20); //手动设置预分配的容量,如果值小于已经自动预分配的容量则不生效 printf("size: %u\n", v.size()); printf("capacity:%u\n", v.capacity()); //此时值就是20了 printf("--------------------------\n"); v.resize(200); //强制设置元素个数是200 printf("size: %u\n", v.size()); //值是200 printf("capacity:%u\n", v.capacity()); //超过预分配的容量后,程序就会再次自动预分配 //string操作 string src("test string"); string dst; // substr(pos, n) pos默认是0,n默认是总长度,所以只传一个参数的时候其实是指定了拷贝的起始位置 dst = src.substr(3); printf("dst:%s\n", dst.c_str()); // dst:"t string" dst = src.substr(); printf("dst:%s\n", dst.c_str()); // dst:"test string" return 0; }
5.C++手册
http://www.cplusplus.com/reference/