《C++新经典》第19章 STL标准模板库大局观
- 19.1 STL总述、发展史、组成与数据结构谈
- 19.1.1 几个概念与推荐书籍
- 19.1.2 算法和数据结构谈
- 19.1.4 标准库使用
- 19.1.5 STL的组成部分
- 19.2 容器分类与array、vector容器精解
- 19.2.1 容器的分类
- 19.2.2 容器的说明和简单应用
- 19.3 容器的说明和简单应用续
- 19.3.1 deque和stack
- 19.3.2 queue
- 19.3.3 list
- 19.3.4 其它
- 19.4 分配器简介、使用与工作原理说
- 19.4.1 分配器简介
- 19.4.2 分配器的使用
- 19.4.3 其它的分配器与原理说
- 19.5 迭代器的概念和分类
- 19.5.1 迭代器基本概念
- 19.5.2 迭代器的分类
- 19.6 算法简介、内部处理与使用范例
- 19.6.1 算法简介
- 19.6.2 算法内部一些处理
- 19.6.3 一些典型算法使用范例
- 19.7 函数对象回顾、系统函数对象与范例
- 19.7.1 函数对象/仿函数回顾
- 19.7.2 标准库中定义的函数对象
- 19.7.3 标准库中定义的函数对象范例
- 19.8 适配器概念、分类、范例与总结
- 19.8.1 适配器基本概念
- 19.8.2 容器适配器
- 19.8.3 算法适配器
- 19.8.4 迭代器适配器
- 19.8.5 总结
19.1 STL总述、发展史、组成与数据结构谈
19.1.1 几个概念与推荐书籍
-
C++标准库
C++ Standard Library,一般安装C++编译器时都会安装。
-
标准模板库
Standard Template Library(STL),包含在C++标准库中(核心)。
-
泛型编程
Generic Programming,使用模板(Template)为主要编程手段来编写代码。
标准模板库就是用泛型编程的编码方式写的一套供程序员非常方便使用的库。
- 推荐书籍
《C++标准库》、《STL源码剖析》。
19.1.2 算法和数据结构谈
-
数据结构浅谈
数据结构,研究数据存取的学问。栈、队列、链表、散列表(哈希表)、树、图等,存取速度快慢,优劣势,具体使用场景等。
-
数据结构学习方法
(1)栈:后进先出。
(2)队列:先进先出。
(3)链表:多个节点链(串)在一起,每个节点都有数据部分和next指针部分。
(4)其它树、图、散列表等。
-
推荐书籍
《算法导论》
19.1.4 标准库使用
#include <iostram>
#include <string>
#include <cstdlib>
#include <vector>
using namespace std;
19.1.5 STL的组成部分
-
容器
vetcor、map、list等。
-
迭代器
用于遍历或者访问容器中的元素,类似指针。
-
算法(函数)
STL提供的一些函数,用来实现一些功能,例如search(查找)、sort(排序)、copy(复制)等。
-
分配器
不太常用,针对频繁分配小块内存时,减少内存空间的浪费,具有一定的提升分配内存效率的作用(内存分配器)。一般使用默认分配器(allocator)。
-
其它
适配器、仿函数(函数对象)等。
19.2 容器分类与array、vector容器精解
19.2.1 容器的分类
容器,保存数据,用于管理一大群元素。
-
顺序容器
Sequence Containers,array、vector、deque、queue、stack、list、forward_list。
-
关联容器
Associative Containers,每一个元素都是一个键值(key/value)对。通过键来找元素,类似小数据库或小字典,查找快速。内部使用树的数据结构来存储数据,按照一定规则(比如根据key)自动存放元素位置,无法控制元素插入位置,只能控制插入内容。set、multiset、map、multimap。
hash_set、hash_multiset、hash_map、hash_multimap等(已过时,用unordered_set、unordered_multiset、unordered_map、unordered_multimap替代)。
-
无序容器
Unordered Contianers,属于关联容器的一种,只在意元素是否在容器内,内部用哈希表实现。
unordered_set、unordered_multiset、unordered_map、unordered_multimap等。
19.2.2 容器的说明和简单应用
- array
顺序容器,数组,空间连续,大小固定。
与内置数组比较:
(1)比内置数组更安全,内置数组不能直接使用拷贝和赋值,array数组可以。
(2)array下标必须是无符号类型,而数组则没有这个限制(可以负数)。
array<int, 10> ial1={0,1,2,3};
array<int, 10> copy=ial1;
#include <array>
array<string, 5> mystring = {"I", "love", "china"};
cout <<mystring.size()<<endl;
cout <<sizeof(mystring)<<endl;
mystring[0] = "long long long";
- vector
空间连续,动态大小,超过容量时,重新分配空间。
建议vector内使用指针,或者提前分配空间。
#include <vector>
#include <iostream>
using namespace std;
class A
{
public:
int m_i;
A(int tmp = 0) : m_i(tmp)
{
cout << "A(int)\n";
}
A(const A &a)
{
m_i = a.m_i;
cout << "A(const A&) \n";
}
~A()
{
cout << "~A()\n";
}
};
#define N 3
int main()
{
{
vector<A> veca;
veca.reverse(N);
for (int i = 0; i < N; i++)
{
cout << "size=" << veca.size() << endl;
cout << "capacity=" << veca.capacity() << endl;
veca[i] = A(i);
}
}
cout << "over";
return 0;
}
19.3 容器的说明和简单应用续
19.3.1 deque和stack
- deque
double-ended queue,双端队列(双向开口)。
分段数组,内存是分段连续的。
#include <iostream>
#include <deque>
using namespace std;
class A
{
public:
A(int tmp) : m_i(tmp)
{
cout << "A(int)" << endl;
}
A(const A &tmpA)
{
m_i = tmpA.m_i;
cout << "A(const A&)" << endl;
}
~A()
{
cout << "~A()" << endl;
}
public:
int m_i;
};
int main()
{
{
deque<A> mydeque;
for (int i = 0; i < 2; i++)
{
cout << "begin" << endl;
mydeque.push_front(A(i));
mydeque.push_back(A(i));
cout << "end" << endl;
}
for (int i = 0; i < mydeque.size(); i++)
{
cout << i << ": " << mydeque[i].m_i << ", address: " << &mydeque[i] << endl;
}
}
cout << "main over" << endl;
return 0;
}
- stack
栈或堆栈。
一端开口,后进先出。
19.3.2 queue
队列,先进先出,一端进,一端出。
19.3.3 list
双向链表,内存空间不连续。
沿链查找元素,查找效率不突出;任意位置插入和删除元素都非常迅速。
19.3.4 其它
-
forward_list
单向链表,push_front,头部插入数据。无push_back。
-
map和set
关联容器,内部实现多为红黑树。
map容器,每个元素都是键值对(key/value),key不能重复,重复需使用multimap。
#include <iostream>
#include <map>
#include <string>
using namespace std;
class A
{
public:
int m_i;
A(int tmp = 0) : m_i(tmp)
{
cout << "A(int)\n";
}
A(const A &a)
{
m_i = a.m_i;
cout << "A(const A&) \n";
}
~A()
{
cout << "~A()\n";
}
};
#define N 3
int main()
{
map<int, string> mymap = {
pair<int, string>(1, "java"),
pair<int, string>(2, "c++"),
{3, "python"},
make_pair(4, "go")};
cout << mymap[4] << endl;
mymap.insert({4, "matlab"});
cout << mymap.at(4) << endl;
mymap[4] = "matlab";
cout << mymap.at(4) << endl;
mymap[5] = "ruby";
cout << mymap.at(5) << endl;
cout << mymap.size() << endl;
cout << mymap[6] << endl;
cout << mymap.size() << endl;
auto iter = mymap.find(3);
if (iter != mymap.end())
{
cout << iter->first << endl;
cout << iter->second << endl;
}
cout << "over";
return 0;
}
set容器中,每个元素都是value,value不能重复,重复需使用multiset。
map、set等关联容器,插入速度慢一些(寻找合适位置),查找速度快。
- unordered_map、unordered_set等
unordered_开头容器属于无序容器,内部一般使用哈希表(散列表)实现。
unordered_map、unordered_set不重复,unordered_multimap、unordered_multiset可重复。
#include <iostream>
#include <unordered_set>
#include <algorithm>
using namespace std;
int main()
{
unordered_set<int> myset;
cout << myset.bucket_count() << endl;
for (int i = 0; i < 8; i++)
myset.insert(i);
cout << myset.bucket_count() << endl;
cout << myset.max_bucket_count() << endl;
cout << myset.size() << endl;
for (int i = 0; i < myset.bucket_count(); i++)
cout << myset.bucket_size(i) << endl;
if (myset.find(5) != myset.end())
cout << "find\n";
if (find(myset.begin(), myset.end(), 5) != myset.end())
cout << "find\n";
cout << "over";
return 0;
}
19.4 分配器简介、使用与工作原理说
19.4.1 分配器简介
vector<int> myvec;
vector<int, std::allocator<int>> myvec;
内存分配器扮演着内存池的角色,大量减少对malloc的调用以减少对内存分配的浪费。
list<int> mylist;
mylist.push_back(10);
mylist.push_back(20);
mylist.push_back(36);
for(auto iter = mylist.begin(); iter != mylist.end(); ++iter){
cout<<*iter<<endl;
int *p=&(*iter);
printf("%p\n", p);
}
19.4.2 分配器的使用
分配器分配内存,写入数据,释放内存。
allocator<int> aalloc;
int *p=aalloc.allocate(3);
int *q=p;
*q=1;q++;
*q=2;q++;
*q=3;
aalloc.deallocate(p, 3);
19.4.3 其它的分配器与原理说
分配器就是一次分配一大块内存,每次使用一小块,用链表将这些内存块管理起来。通过内存池的内存分配机制,有效减少调用malloc次数,减少内存浪费,提高程序运行效率。
list<int, allocator<int>> mylist1;
list<double, allocator<double>> mylist2;
list<int, allocator<int>> mylist3;
19.5 迭代器的概念和分类
19.5.1 迭代器基本概念
可遍历STL容器全部或部分元素的对象(行为类似于指针对象,*iter获取迭代器所指向的内容)。
迭代器由容器提供(容器里面定义者迭代器的具体类型细节),用来表现容器中的某一个位置。
#include <iostream>
#include <vector>
using namespace std;
int main()
{
vector<int> iv = {1, 2, 3};
for (vector<int>::iterator iter = iv.begin(); iter != iv.end(); ++iter)
{
*iter += 6;
cout << *iter << endl;
}
cout << "over";
return 0;
}
19.5.2 迭代器的分类
依据迭代器的移动特性和操作进行分类。
(1)输出型迭代器(Output iterator)
struct output_iterator_tag
(2)输入型迭代器(Input iterator)
struct input_iterator_tag
(3)前向型迭代器(Input iterator)
struct forward_iterator_tag
(4)双向迭代器(Bidirectional iterator)
struct bidirectional_iterator_tag
(5)随机访问迭代器(Random-access iterator)
struct random_access_iterator_tag
struct output_iterator_tag
继承关系:
struct input_iterator_tag
↓
struct forward_iterator_tag
↓
struct bidirectional_iterator_tag
↓
struct random_access_iterator_tag
多数容器有迭代器,stack、queue等容器不提供迭代器。
迭代器种类 | 迭代器能力 | 提供该迭代器的容器 |
---|
output_iterator_tag | 向前写入 | ostream |
input_iterator_tag | 向前读取一次 | istream |
forward_iterator_tag | 向前读取 | forward_list、nordered |
bidirectional_iterator_tag | 向前和向后读取 | list、set、multiset、map、multimap |
random_access_iterator_tag | 随机读取(跳过一定个数的元素) | array、vector、deque、string、C风格数组 |
#include <iostream>
#include <array>
#include <vector>
#include <list>
#include <map>
#include <set>
using namespace std;
void _display_category(output_iterator_tag mytag)
{
cout << "output_iterator_tag" << endl;
}
void _display_category(input_iterator_tag mytag)
{
cout << "input_iterator_tag" << endl;
}
void _display_category(forward_iterator_tag mytag)
{
cout << "forward_iterator_tag" << endl;
}
void _display_category(bidirectional_iterator_tag mytag)
{
cout << "bidirectional_iterator_tag" << endl;
}
void _display_category(random_access_iterator_tag mytag)
{
cout << "random_access_iterator_tag" << endl;
}
template <typename T>
void display_category(T iter)
{
cout << "begin" << endl;
typename iterator_traits<T>::iterator_category cagy;
_display_category(cagy);
cout << "typeid(iter).name()=" << typeid(iter).name() << endl;
cout << "end" << endl;
}
int main()
{
display_category(array<int, 100>::iterator());
display_category(vector<int>::iterator());
display_category(list<int>::iterator());
display_category(map<int, int>::iterator());
display_category(set<int>::iterator());
cout << "over";
return 0;
}
迭代器功能整理:
(1)输出型迭代器(Output iterator)
struct output_iterator_tag
一步步往前走,通过这个迭代器往容器中写数据。
*iter = value,value写入迭代器指向的位置。
++iter
iter++
(2)输入型迭代器(Input iterator)
struct input_iterator_tag
一步步往前走,通过这个迭代器顺序返回元素值。
*iter,读取元素值
iter->member,读取元素成员
++iter
iter++
iter1 == iter2
iter1 != iter2
(3)前向型迭代器(Input iterator)
struct forward_iterator_tag
继承input_iterator_tag,同时支持写入操作
*iter,读取元素值
iter->member,读取元素成员
*iter = value,value写入迭代器指向的位置。
++iter
iter++
iter1 == iter2
iter1 != iter2
iter1 = iter2,迭代器赋值
(4)双向迭代器(Bidirectional iterator)
struct bidirectional_iterator_tag
继承forward_iterator_tag,增加向回(反向)迭代,即迭代位置可以往回退。
*iter,读取元素值
iter->member,读取元素成员
*iter = value,value写入迭代器指向的位置。
++iter
iter++
iter1 == iter2
iter1 != iter2
–iter
iter–
(5)随机访问迭代器(Random-access iterator)
struct random_access_iterator_tag
继承bidirectional_iterator_tag,增加随机访问能力,即增减偏移量,同时能够计算距离,支持一些关系运算等。
*iter,读取元素值
iter->member,读取元素成员
*iter = value,value写入迭代器指向的位置。
++iter
iter++
iter1 == iter2
iter1 != iter2
–iter
iter–
iter[n]
iter+=n
iter-=n
iter+n
iter-n
iter1-iter2,计算距离
iter1<iter2, iter1>iter2
iter1<=iter2, iter1>=iter2
19.6 算法简介、内部处理与使用范例
19.6.1 算法简介
19.6.2 算法内部一些处理
算法是函数模板,接收各种类型的迭代器作为形参。算法内部根据迭代器类型,有不同处理,处于算法执行效率的考虑。
19.6.3 一些典型算法使用范例
#include <algorithm>
- for_each
void func(int i){
cout<<i<<endl;
}
vector<int> myvector = {10, 20, 30, 40, 50};
for_each(myvector.begin(), myvector.end(), func);
template<class InputIterator, class Function>
Function for_each(InputIterator first, InputIterator last, Function f){
for(; first != last; ++first)
f(*first);
return f;
}
- find
vector<int> myvector = {10, 20, 30, 40, 50};
vector<int>::iterator finditer = find(myvector.begin(), myvector.end(), 400);
if(finditer != myvector.end())
cout<<"find"<<endl;
else
cout<<"not find"<<endl;
//优先使用容器自带(若存在)的同名函数。
map<int, string> mymap;
mymap.insert(std::make_pair(1, "wang"));
mymap.insert(std::make_pair(2, "li"));
auto finditer = mymap.find(2);
if(finditer != mymap.end()){
cout<<"find"<<endl;
cout<<iter->first<<iter->second<<endl;
}
- find_if
vector<int> myvector = {10, 20, 30, 40, 50};
auto finditer = find_if(myvector.begin(), myvector.end(), [](int val){
if(val>15)
return true;
return false;
});
if(finditer != myvector.end())
cout<<"find"<<*finditer<<endl;
else
cout<<"not find"<<endl;
- sort
vector<int> myvector = {10, 20, 30, 40, 50};
sort(myvector.begin(), myvector.begin()+3);
bool myfuncsort(int i, int j){
return i>j;
}
sort(myvector.begin(), myvector.end(), myfuncsort);
class A{
public:
bool operator()(int i, int j){
return i>j;
}
};
A a;
sort(myvector.begin(), myvector.end(), a);
list<int> mylist = {10, 20, 30, 40, 50};
mylist.sort(myfuncsort);
顺序容器可以排序,关联容器(含无序容器)不适合排序。
map<int, string> mymap;
mymap.insert(make_pair(50, "wang"));
mymap.insert(make_pair(15, "li"));
mymap.insert(pair<int, string>(80, "zhao"));
unordered_set<int> myset = {10, 20, 30, 40, 50};
19.7 函数对象回顾、系统函数对象与范例
19.7.1 函数对象/仿函数回顾
函数对象(function objects)或仿函数(functors),配合算法使用实现特定功能,调用方式很统一,即:名字(参数列表)。
函数对象形式整理
- 函数:void func(int x) {…}
- 可调用对象(类/类模板生成对象):class A{public:void operator()(int x){}};
- lambda表达式:[](int x){};
19.7.2 标准库中定义的函数对象
#include <functional>
- 算术运算类
表达式(函数对象) | 效果 |
---|
negate<类型>() | -param |
plus<类型>() | param1+param2 |
minus<类型>() | param1-param2 |
multiplies<类型>() | param1*param2 |
divides<类型>() | param1/param2 |
modulus<类型>() | param1%param2 |
- 关系运算类
表达式(函数对象) | 效果 |
---|
equal_to<类型>() | param1==param2 |
not_equal_to<类型>() | param1!=param2 |
less<类型>() | param1<param2 |
greater<类型>() | param1>param2 |
less_equal<类型>() | param1<=param2 |
greater_equal<类型>() | param1>=param2 |
- 逻辑运算符
表达式(函数对象) | 效果 |
---|
logical_not<类型>() | !param |
logical_and<类型>() | param1&¶m2 |
logical_or<类型>() | param1||param2 |
- 位运算类
表达式(函数对象) | 效果 |
---|
bit_xor<类型>() | param1^param2 |
bit_and<类型>() | param1¶m2 |
bit_or<类型>() | param1|param2 |
template<class _Ty = void>
struct plus{
constexpr _Ty operator()(const _Ty& _Left, const _Ty& _Right) const {
return _Left + _Right;
}
};
cout<<plus<int>()(4, 5)<<endl;
19.7.3 标准库中定义的函数对象范例
vector<int> myvector = {10, 20, 30, 40, 50};
sort(myvector.begin(), myvector.end(), greater<int>());
sort(myvector.begin(), myvector.end(), less<int>());
19.8 适配器概念、分类、范例与总结
19.8.1 适配器基本概念
类似于转接头。将一个既有东西进行适当改造,增减一点东西,就会成为一个适配器。
19.8.2 容器适配器
stack和queue归类为容器适配器,将既有的deque进行适当的改造(阉割一点东西)。
template<class _Ty, class _Container = deque<_Ty>>
class queue {
public:
void push(value_type && _Val){
c.push_back(_STD move(_Val));
}
protected:
_Container c;
};
19.8.3 算法适配器
算法是函数模板,算法适配器就是函数适配器,最典型的是绑定器(Binder)。
bind,函数模板,算法适配器中的绑定器。
vector<int> myvector = {50, 15, 80, 30, 46, 80};
int cishu = count(myvector.begin(), myvector.end(), 80);
cout << cishu <<endl;
class A{
public:
bool operator()(int i){
return i>40;
}
};
A a;
cishu = count_if(myvector.begin(), myvector.end(), a);
template<class _Ty = void>
struct less{
constexpr bool operator()(const _Ty& _Left, const _Ty& _Right) const {
return _Left < _Right;
}
};
class A{
public:
bool operator()(int i){
return 40<i;
}
};
auto bf = bind(less<int>(), 40, placeholders::_1);
bf(19);
cishu = count_if(myvector.begin(), myvector.end(), bind(less<int>(), 40, placeholders::_1));
19.8.4 迭代器适配器
方向迭代器就是一个迭代器适配器。
vector<int> iv = {100, 200, 300};
for(vector<int>::reverse_iterator riter = iv.rbegin(); riter != iv.rend(); riter++)
cout<<*riter<<endl;
19.8.5 总结
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)