一、STL 六大部件(Components)
- 容器(Containers)
- 分配器(Allocators)
- 算法(Algorithms)
- 迭代器(Iterators)
- 适配器(Adapters)
- 仿函数(Functors)
六大部件的关系目前可以初步用下图来表示:
下面我们用一个程序来使用这六大部件
1. 前闭后开区间
对于迭代器来说,begin() 指向的是容器的第一个元素,end() 指向容器最后一个元素的下一个位置,所以经常使用 end() 来判断是否遍历到了容器结尾。如下图所示:
Container<T> c;
...
Container<T>::iterator it = c.begin();
for(; it != c.end(); ++it)
...
2. range-based for statement (since C++11)
【语法】
for(decl : coll)
{
statement;
}
【举例】
vector<int> vec;
...
for(auto elem : vec)
{
cout << elem << endl; // 打印vec中每一个元素
}
for(auto &elem : vec)
{
elem *= 3;
}
3. auto 关键字(since C++11)
list<string> c;
...
list<string>::iterator it;
it = find(c.begin(), c.end(), target);
【等同于】
list<string> c;
...
auto it = find(c.begin(), c.end(), target);
二、容器之分类
其实容器主要分为两大类:序列容器和关联容器。但是 C++11 后新增了 Unordered Containers,其实它的本质还是属于关联容器。
1. 序列容器(Sequence Containers)
所谓序列式容器,其中的元素都可序,但未必有序,C++ 语言本来提供了一种序列式容器 array,STL 另外再提供 vector,list,deque,stack,queue,priority-queue 等等序列式容器。其中 stack 和 queue 由于只是将 deque 改头换面,技术上被归类为一种配接器(Adapter)。
1. Array(数组)(C++11)
在 C/C++ 里面本来就存在数组,它是语言的一部分。在 C++11 中把数组也变成一个 class。Array在内存中是连续分配的, 当初你定义了多大空间它就有多大空间,它是一个定长数组,当数组中空间不够时,它是没有办法扩充的。
2. Vector
单向开口的动态数组,在内存中是连续分配的。当 vector 中空间不够时,它会自动扩充(分配器)。
3. Deque
双向开口的的队列,简称双端队列。deque 是由分段的连续线性空间组合而成
4. List
双向环状链表,在内存中不一定是连续存储的。
5. Forward-List(C++11)
单向链表,
2. 关联容器(Associative Containers)
标准的 STL 关联式容器分为 set(集合)和 map(映射)两大类,以及这两大类的衍生体 mutiset(多键集合)和 multimap(多键映射表)。这些容器的底层均以 RB-tree(红黑树)完成。RB-tree 也是一个独立容器,但并不开放给外界使用。
此外,SGI STL 还提供了一个不再标准规格之列的关联式容器:hash table(散列表),以及以此 hash table 为底层实现机制而完成的 hash_set(散列集合)、hash_map(散列映射表)、hash_multiset(散列多键集合)和 hash_multimap(散列多键映射表)。
该容器下元素由 key 和 value 组成。key 为关键值,通过 key 可以很快找到 value 的值。所以关联容器很适合做快速的查找。
1. Set/Multiset
set 的 key 就是 value。set 中的元素不允许重复,如果想要重复,使用 multiset。
2. Map/Multimap
map 由 key 和 value 组成。map 中的元素的键值不允许重复,如果想要重复,使用 multimap。
3. 不定序容器(Unordered Containers)(C++11)
其实 Unordered Containers 也是一种关联式容器。底层是使用 hashtable 实现。
目前,hashTable 大多都采用很多链表组成。HashTable Separate Chaining 数据结构如下图所示:
1. Unordered Set/Multiset
2. Unordered Map/Multimap
三、容器测试
1. 使用容器 array
#include <iostream>
#include <array>
#include <ctime>
#include <cstdlib>
using namespace std;
#define ASIZE 500000
// 随机生成 1-500000 之间的数
long get_a_target_long()
{
return rand() % 500000 + 1;
}
int compareLongs(const void *p1, const void *p2)
{
return *(long *)p1 < *(long *)p2;
}
void test_array()
{
cout << "\ntest array() ..............\n";
array<long, ASIZE> arr;
clock_t startTime = clock();
for(long i = 0; i < ASIZE; i++)
arr[i] = rand() % 500000 + 1;
cout << "milli-seconds:" << (clock() - startTime) << endl;
cout << "array.size():" << arr.size() << endl;
cout << "array.front():" << arr.front() << endl;
cout << "array.back():" << arr.back() << endl;
cout << "array.data():" << arr.data() << endl;
int target = get_a_target_long();
startTime = clock();
// 排序
// void qsort (void* base, size_t num, size_t size, int (*compar)(const void*,const void*));
qsort(arr.data(), ASIZE, sizeof(long), compareLongs);
// 二分查找:使用二分查找之前一定要先排序
// void* bsearch (const void* key, const void* base, size_t num, size_t size, int (*compar)(const void*,const void*));
long *pItem = (long *)bsearch(&target, (arr.data()), ASIZE, sizeof(long), compareLongs);
cout << "qsort() + bsearch(), milli-seconds:" << (clock() - startTime) << endl;
if(pItem)
cout << "found:" << *pItem << endl;
else
cout << "Not Found!" << endl;
}
int main()
{
test_array();
return 0;
}
2. 使用容器 vector
#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>
#include <ctime>
#include <cstdlib>
using namespace std;
// ****************** vector ********************//
string get_a_target_string()
{
srand(time(nullptr));
long target = rand() % 50000 + 1;
char buf[10];
snprintf(buf, 10, "%d", target);
return string(buf);
}
int compareString(const void *str1, const void *str2)
{
return *(string *)str1 < *(string *)str2;
}
void test_vector(long &value)
{
cout << "\ntest_vector()...................... \n";
vector<string> str;
char buf[10];
clock_t startTime = clock();
for(long i = 0; i < value; i++)
{
try{ // 当内存不够时,捕获异常
snprintf(buf, 10, "%d", rand());
str.push_back(string(buf));
}
catch(exception &p){
cout << "i = " << i << " " << p.what() << endl;
abort();
}
}
cout << "milli-seconds:" << (clock() - startTime) << endl;
cout << "vector.size():" << str.size() << endl;
cout << "vector.front():" << str.front() << endl;
cout << "vector.back():" << str.back() << endl;
cout << "vector.data():" << str.data() << endl;
cout << "vector.capacity():" << str.capacity() << endl; // 一定比 size 大
string target = get_a_target_string();
startTime = clock();
auto pItem = find(str.begin(), str.end(), target); // find 返回迭代器
cout << "find, milli-seconds:" << (clock() - startTime) << endl;
if(pItem != str.end())
cout << "find:" << *pItem << endl;
else
cout << "Not found" << endl;
}
// ****************** vector ********************//
int main()
{
long target = 1000000;
test_vector(target);
return 0;
}
3. 使用容器 list
#include <iostream>
#include <vector>
#include <list>
#include <algorithm>
#include <functional>
#include <ctime>
#include <cstdlib>
using namespace std;
// ****************** list ********************//
void test_list(long &value)
{
cout << "\ntest_list()...................... \n";
list<string> str;
char buf[10];
clock_t startTime = clock();
for(long i = 0; i < value; i++)
{
try{ // 当内存不够时,捕获异常
snprintf(buf, 10, "%d", rand());
str.push_back(string(buf));
}
catch(exception &p){
cout << "i = " << i << " " << p.what() << endl;
abort();
}
}
cout << "milli-seconds:" << (clock() - startTime) << endl;
cout << "list.size():" << str.size() << endl;
cout << "list.max_size():" << str.max_size() << endl;
cout << "list.front():" << str.front() << endl;
cout << "list.back():" << str.back() << endl;
string target = get_a_target_string();
startTime = clock();
auto pItem = find(str.begin(), str.end(), target);
cout << "find, milli-seconds:" << (clock() - startTime) << endl;
if(pItem != str.end())
cout << "find:" << *pItem << endl;
else
cout << "Not found" << endl;
startTime = clock();
str.sort(); // 调用容器内部的 sort 方法,不是标准库中的 sort。
cout << "str.sort(), mill-seconds:" << (clock() - startTime) << endl;
}
// ****************** list ********************//
int main()
{
long target = 1000000;
test_list(target);
return 0;
}
4. 使用容器 forward_list
#include <iostream>
#include <vector>
#include <list>
#include <forward_list>
#include <algorithm>
#include <functional>
#include <ctime>
#include <cstdlib>
using namespace std;
// ****************** forward_list ********************//
void test_forward_list(long &value)
{
cout << "\ntest_forward_list()...................... \n";
forward_list<string> str;
char buf[10];
clock_t startTime = clock();
for(long i = 0; i < value; i++)
{
try{ // 当内存不够时,捕获异常
snprintf(buf, 10, "%d", rand());
str.push_front(string(buf)); // 只有 push_front() 方法,没有 push_back() 方法
}
catch(exception &p){
cout << "i = " << i << " " << p.what() << endl;
abort();
}
}
cout << "milli-seconds:" << (clock() - startTime) << endl;
cout << "forward_list.max_size():" << str.max_size() << endl;
cout << "forward_list.front():" << str.front() << endl;
//cout << "forward_list.back():" << str.back() << endl; // 没有此函数
//cout << "forward_list.size():" << str.size() << endl; // 没有此函数
string target = get_a_target_string();
startTime = clock();
auto pItem = find(str.begin(), str.end(), target);
cout << "find, milli-seconds:" << (clock() - startTime) << endl;
if(pItem != str.end())
cout << "find:" << *pItem << endl;
else
cout << "Not found" << endl;
startTime = clock();
str.sort();
cout << "str.sort(), mill-seconds:" << (clock() - startTime) << endl;
}
// ****************** forward_list ********************//
int main()
{
long target = 1000000;
test_forward_list(target);
return 0;
}
注意:只有 list 和 forward_list 才有自带的排序方法。
5. 使用容器 deque
deque 是由分段的连续线性空间组合而成。
#include <iostream>
#include <vector>
#include <list>
#include <forward_list>
#include <deque>
#include <algorithm>
#include <functional>
#include <ctime>
#include <cstdlib>
using namespace std;
// ****************** deque ********************//
void test_deque(long &value)
{
cout << "\ntest_forward_list()...................... \n";
deque<string> str;
char buf[10];
clock_t startTime = clock();
for(long i = 0; i < value; i++)
{
try{ // 当内存不够时,捕获异常
snprintf(buf, 10, "%d", rand());
str.push_front(string(buf));
}
catch(exception &p){
cout << "i = " << i << " " << p.what() << endl;
abort();
}
}
cout << "milli-seconds:" << (clock() - startTime) << endl;
cout << "deque.size():" << str.size() << endl;
cout << "deque.front():" << str.front() << endl;
cout << "deque.back():" << str.back() << endl;
cout << "deque.max_size():" << str.max_size() << endl;
string target = get_a_target_string();
startTime = clock();
auto pItem = find(str.begin(), str.end(), target);
cout << "find, milli-seconds:" << (clock() - startTime) << endl;
if(pItem != str.end())
cout << "find:" << *pItem << endl;
else
cout << "Not found" << endl;
startTime = clock();
sort(str.begin(), str.end());
cout << "sort(), mill-seconds:" << (clock() - startTime) << endl;
}
// ****************** deque ********************//
int main()
{
long target = 1000000;
test_deque(target);
return 0;
}
6. 使用容器 multiset
#include <list>
#include <forward_list>
#include <deque>
#include <algorithm>
#include <functional>
#include <ctime>
#include <cstdlib>
#include <set>
#include <map>
using namespace std;
// ****************** multiset ********************//
void test_multiset(long &value)
{
cout << "\ntest_multiset()...................... \n";
multiset<string> str;
char buf[10];
clock_t startTime = clock();
for(long i = 0; i < value; i++)
{
try{ // 当内存不够时,捕获异常
snprintf(buf, 10, "%d", rand());
str.insert(string(buf));
}
catch(exception &p){
cout << "i = " << i << " " << p.what() << endl;
abort();
}
}
cout << "milli-seconds:" << (clock() - startTime) << endl;
cout << "multise.size():" << str.size() << endl;
cout << "multise.max_size():" << str.max_size() << endl;
string target = get_a_target_string();
startTime = clock();
auto pItem = find(str.begin(), str.end(), target); // 采用算法中的 find
cout << "find, milli-seconds:" << (clock() - startTime) << endl;
if(pItem != str.end())
cout << "find:" << *pItem << endl;
else
cout << "Not found" << endl;
startTime = clock();
auto it = str.find(target); // 调用容器自身的 find
cout << "find, milli-seconds:" << (clock() - startTime) << endl;
if(it != str.end())
cout << "find:" << *it << endl;
else
cout << "Not found" << endl;
}
// ****************** multiset ********************//
int main()
{
long target = 1000000;
test_multiset(target);
return 0;
}
7. 使用容器 multimap
#include <list>
#include <forward_list>
#include <deque>
#include <algorithm>
#include <functional>
#include <ctime>
#include <cstdlib>
#include <set>
#include <map>
using namespace std;
// ****************** multimap ********************//
void test_multimap(long &value)
{
cout << "\ntest_multimap()...................... \n";
multimap<long, string> str;
char buf[10];
clock_t startTime = clock();
for(long i = 0; i < value; i++)
{
try{ // 当内存不够时,捕获异常
snprintf(buf, 10, "%d", rand());
// multimap 不可使用 [] 做 insertion
str.insert(pair<long, string>(i, buf));
//str.insert({i, buf}); // 第二种插入方式
}
catch(exception &p){
cout << "i = " << i << " " << p.what() << endl;
abort();
}
}
cout << "milli-seconds:" << (clock() - startTime) << endl;
cout << "multimap.size():" << str.size() << endl;
cout << "multimap.max_size():" << str.max_size() << endl;
long target = get_a_target_long();
startTime = clock();
auto it = str.find(target);
cout << "find, milli-seconds:" << (clock() - startTime) << endl;
if(it != str.end())
cout << "find, value:" << it->second << endl;
else
cout << "Not found" << endl;
}
// ****************** multimap ********************//
int main()
{
long target = 1000000;
test_multimap(target);
return 0;
}
8. 使用容器 unordered_multiset
// ****************** unordered_multiset ********************//
void test_unordered_multiset(long &value)
{
cout << "\ntest_unordered_multiset()...................... \n";
unordered_multiset<string> str;
char buf[10];
clock_t startTime = clock();
for(long i = 0; i < value; i++)
{
try{ // 当内存不够时,捕获异常
snprintf(buf, 10, "%d", rand());
str.insert(string(buf)); // 只有 push_front() 方法,没有 push_back() 方法
}
catch(exception &p){
cout << "i = " << i << " " << p.what() << endl;
abort();
}
}
cout << "milli-seconds:" << (clock() - startTime) << endl;
cout << "unordered_multiset.size():" << str.size() << endl;
cout << "unordered_multiset.max_size():" << str.max_size() << endl;
cout << "unordered_multiset.bucket_count():" << str.bucket_count() << endl; // 篮子的个数
cout << "unordered_multiset.load_factor():" << str.load_factor() << endl;
cout << "unordered_multiset.max_load_factor():" << str.max_load_factor() << endl;
cout << "unordered_multiset.max_bucket_count():" << str.max_bucket_count() << endl;
string target = get_a_target_string();
startTime = clock();
auto pItem = find(str.begin(), str.end(), target);
cout << "find, milli-seconds:" << (clock() - startTime) << endl;
if(pItem != str.end())
cout << "find:" << *pItem << endl;
else
cout << "Not found" << endl;
startTime = clock();
auto it = str.find(target);
cout << "find, milli-seconds:" << (clock() - startTime) << endl;
if(it != str.end())
cout << "find:" << *it << endl;
else
cout << "Not found" << endl;
}
// ****************** unordered_multiset ********************//
int main()
{
long target = 1000000;
test_unordered_multiset(target);
return 0;
}
9. 使用容器 unordered_multimap
// ****************** unordered_multimap ********************//
void test_unordered_multimap(long &value)
{
cout << "\ntest_unordered_multimap()...................... \n";
unordered_multimap<long, string> str;
char buf[10];
clock_t startTime = clock();
for(long i = 0; i < value; i++)
{
try{ // 当内存不够时,捕获异常
snprintf(buf, 10, "%d", rand());
//str.insert(pair<long, string>(i, buf)); // 只有 push_front() 方法,没有 push_back() 方法
str.insert({i, buf});
}
catch(exception &p){
cout << "i = " << i << " " << p.what() << endl;
abort();
}
}
cout << "milli-seconds:" << (clock() - startTime) << endl;
cout << "unordered_multimap.size():" << str.size() << endl;
cout << "unordered_multimap.max_size():" << str.max_size() << endl;
long target = get_a_target_long();
startTime = clock();
auto it = str.find(target);
cout << "find, milli-seconds:" << (clock() - startTime) << endl;
if(it != str.end())
cout << "find, value:" << it->second << endl;
else
cout << "Not found" << endl;
}
// ****************** unordered_multimap ********************//
int main()
{
long target = 1000000;
test_unordered_multimap(target);
return 0;
}
10. 使用容器 set
// ****************** multiset ********************//
void test_set(long &value)
{
cout << "\ntest_set()...................... \n";
set<string> str;
char buf[10];
clock_t startTime = clock();
for(long i = 0; i < value; i++)
{
try{ // 当内存不够时,捕获异常
snprintf(buf, 10, "%d", rand());
str.insert(string(buf)); // 只有 push_front() 方法,没有 push_back() 方法
}
catch(exception &p){
cout << "i = " << i << " " << p.what() << endl;
abort();
}
}
cout << "milli-seconds:" << (clock() - startTime) << endl;
cout << "set.size():" << str.size() << endl;
cout << "set.max_size():" << str.max_size() << endl;
string target = get_a_target_string();
startTime = clock();
auto pItem = find(str.begin(), str.end(), target);
cout << "find, milli-seconds:" << (clock() - startTime) << endl;
if(pItem != str.end())
cout << "find:" << *pItem << endl;
else
cout << "Not found" << endl;
startTime = clock();
auto it = str.find(target);
cout << "find, milli-seconds:" << (clock() - startTime) << endl;
if(it != str.end())
cout << "find:" << *it << endl;
else
cout << "Not found" << endl;
}
// ****************** set ********************//
int main()
{
long target = 1000000;
test_set(target);
return 0;
}
我们申请了 1000000 个元素,但是 set 实际元素个数为 32768。
11. 使用容器 map
//****************** map ********************//
void test_map(long &value)
{
cout << "\ntest_map()...................... \n";
map<long, string> str;
char buf[10];
clock_t startTime = clock();
for(long i = 0; i < value; i++)
{
try{ // 当内存不够时,捕获异常
snprintf(buf, 10, "%d", rand());
str[i] = string(buf);
}
catch(exception &p){
cout << "i = " << i << " " << p.what() << endl;
abort();
}
}
cout << "milli-seconds:" << (clock() - startTime) << endl;
cout << "map.size():" << str.size() << endl;
cout << "map.max_size():" << str.max_size() << endl;
long target = get_a_target_long();
startTime = clock();
auto it = str.find(target);
cout << "find, milli-seconds:" << (clock() - startTime) << endl;
if(it != str.end())
cout << "find, value:" << it->second << endl;
else
cout << "Not found" << endl;
}
// ****************** map ********************//
int main()
{
long target = 1000000;
test_map(target);
return 0;
}