STL标准库简介
STL(Standard Template LIbrary)
- STL算法是泛型的(generic),不与任何特定数据结构和对象绑定,不必在环境类似的情况下重写代码;
- STL算法可以量身定做,并且具有很高的效率;
- STL可以进行扩充,你可以编写自己的组件并且能与STL标准的组件进行很好的配合;
序列容器和函数对象基本使用
容器
- 容器用于存放数据;STL的容器分为两大类:
- 序列式容器(Sequence Containers):
其中的元素都是可排序的(ordered),STL提供了vector、list、deque等序列式容器,而stack、queue、priority_queue则是容器适配器; - 关联式容器(Associative Containers):
每个数据元素都是由一个键(key)和值(value)组成,当元素被插入到容器时,按其键以某种特定规则放入适当位置;常见的STL关联容器如:set、multiset、map、multimap
#include<vector>
#include<list>
#include<queue>
#include<stack>
#include<iostream>
using namespace std;
struct Display//仿函数
{
void operator()(int i)
{
cout<<i<<" ";
}
}
int main()
{
int iArr[] = {
1,2,3,4,5};
vector<int> iVector(iArr,iArr+4);
list<int> iList(iArr,iArr+4);
deque<int> iDeque(iArr,iArr+4);
queue<int> iQueue(iDeque); //队列 先进先出
stack<int> iStack(iDeque); //栈 先进后出
priority_queue<int> iPQueue(iArr,iArr+4); //优先队列 按优先权
for_each(iVector.begin(),iVector.end(),Display()); //1 2 3 4
cout<<endl;
for_each(iList.begin(),iList.end(),Display()); //1 2 3 4
cout<<endl;
for_each(iDeque.begin(),iDeque.end(),Display()); //1 2 3 4
cout<<endl;
while(!iQueue.empty())
{
cout<<iQueue.front()<<" "; //1 2 3 4
iQueue.pop();
}
while(!iStack.empty())
{
cout<<iStack.top()<<" "; //4 3 2 1
iStack.pop();
}
while(!iPQueue.empty())
{
cout<<iPQueue.top()<<" "; //4 3 2 1
iPQueue.pop();
}
return 0;
}
关联容器map的基本使用和查找
#include<map>
#include<string>
#include<algorithm>
#include<iostream>
using namespace std;
struct Display()
{
void operator()(pair<string,double> info)
{
cout<<info.first<<info.second<<endl;
}
}
int main()
{
map<string,double> studentSocres;
studentSocres['LiMing'] = 95.0;
studentSocres['LiHong'] = 95.0;
studentSocres.insert(pair<string,double>("zhangsan",100.0));
studentSocres.insert(pair<string,double>("Lisi",98.6));
studentSocres.insert(pair<string,double>("wangwu",94.5));
studentSocres.insert(map<string,double>::value_type("zhangsan",94.5));
//value_type 是STL里面的属性
for_each(studentSocres.begin(),studentSocres.end(),Display()); //第3个参数是函数对象
map<string,double>::iterator iter;
iter = studentSocres.find("wangwu");//会返回一个指针
if(iter != studentSocres.end())
{
cout<<"Found the values is:"<<iter->second<<endl;
}else{
cout<<"Didn't find the key."<<endl;
}
return 0;
}
关联容器的插入删除及迭代器失效问题
字典的值进行两次插入,第二次插入会直接失败
注意迭代器失效的问题。
//使用迭代器完成遍历查找的过程
iter = studentSocres.begin();
while(iter != studentSocres.end())
{
if(abs(iter->second < 95.0)
{
//小于95的进行删除
studentSocres.erase(iter++);//先删除当前元素,迭代器往后走一个(迭代器失效问题)
}else{
iter++;
}
}
//使用for循环处理迭代器
for(iter = studentSocres.begin();iter != studentSocres.end();iter++)
{
if(iter->second <= 98.5)
{
iter = studentSocres.erase(iter);//注意:迭代器失效问题
}
}
//find得到并删除元素
iter = studentSocres.find("LiHong");
studentSocres.erase(iter);
for_each(studentSocres.begin(),studentSocres.end(),Display());
int n = studentSocres.erase("LiHong1");
cout<<n<<endl; //成功删除的个数
//删除范围元素
studentSocres.erase(studentSocres.begin(),studentSocres.end());
从函数指针到防函数模板
仿函数(functor)
- 仿函数一般不会单独使用,主要是为了搭配STL算法使用。
- 函数指针不能满足STL对抽象性的要求,不能满足软件积木的要求,无法和STL其他组件搭配;
- 本质就是类重载了一个operator(),创建一个行为类似函数的对象;
#include<algorithm>
#include<iostream>
using namespace stdl
//C++方式
inline bool MySort(int a,int b)
{
return a < b;//顺序
}
inline void Display(int a)
{
couut<<a<<" ";
}
//C++泛型
template<class T>
inline bool MySortT(T const& a,T const& b)
{
return a < b;//顺序
}
template<class T>
inline void DisplayT(T const& a)
{
couut<<a<<" ";
}
//C++仿函数
struct SortF
{
bool operator()(int a,int b)
{
return a < b;
}
}
struct DisplayF
{
void operator()(int a)
{
cout<<a<<" ";
}
}
//C++ 仿函数模板
template<class T>
struct SortTF
{
inline bool operator()(T const& a,T const& b) const
{
return a < b;
}
}
template<class T>
struct DisplayTF
{
inline void operator()(T const& a) const
{
cout<<a<<" ";
}
}
int main()
{
//C++方式
int arr[] = {
4,3,2,1,7};
sort(arr,arr+5,MySort);
for_each(arr,arr+5,display);
cout<<endl;
//C++泛型
int arr2[] = {
4,3,2,1,7};
sort(arr2,arr2+5,MySortT<int>);
for_each(arr2,arr2+5,displayT<int>);
return 0;
//C++仿函数
int arr3[] = {
4,3,2,1,7};
sort(arr3,arr3+5,SortF());
for_each(arr3,arr3+5,displayF());
//C++仿函数模板
int arr4[] = {
4,3,2,1,7};
sort(arr4,arr4+5,SortTF<int>());
for_each(arr4,arr4+5,displayTF<int>());
return 0;
}
算法transform和lambda表达式
算法(algorithm)
- STL中算法大致分为四类:包含于<algorithm>,<numeric>,<functional>
- 非可变序列算法:指不直接修改其所操作的容器内容的算法;
- 可变序列算法:指可以修改它们所操作的容器内容的算法;
- 排序算法:包括对序列进行排序和合并的算法、搜索算法以及有序序列上的集合操作;
- 数值算法:对容器内容进行数值计算;
- 最常见的算法包括:
查找,排序和通用算法,排列组合算法,数值算法,集合算法等算法;
#include<algorithm>
#include<functional>
#include<numeric>
#include<iostream>
using namespace std;
int main()
{
int ones[] = {
1,2,3,4,5};
int twos[] = {
10,20,30,40,50};
int results[5];
transform(ones,ones+5,twos,results,std::plus<int>());//注意twos和results的长度不得低于5
for_each(results,results+5,[](int a)->void {
cout<<a<<endl;});//lambda表达式(匿名函数)
return 0;
}
lambda表达式
[](int a)->void {
cout<<a<<endl;}
[]
:表示需要用到的外部参数(int a)
:表示函数参数列表void
:返回值类型{cout<<a<<endl;}
:函数体
STL容器的统计与二分查找
#include<algorithm>
#include<functional>
#include<numeric>
#include<vector>
#include<iostream>
using namespace std;
int main()
{
//find
int arr[] = {
0,1,2,3,3,4,4,5,6,6,7,7,7,8};
int len = sizeof(arr)/sizeof(arr[0]);
vector<int> iA(arr+2,arr+6);//2,3,3,4
cout<<count(arr,arr+len,7)<<endl; //统计7的个数
cout<<endl;
cout<<count_if(arr,arr+sizeof(arr)/sizeof(arr[0]),bind2nd(less<int>(),7))<<endl; //统计小于7的个数 < 7
cout<<count_if(arr,arr+sizeof(arr)/sizeof(arr[0]),bind1st(less<int>(),7))<<endl; //统计大于7的个数 7 <
cout<<binary_search(arr,arr+len,9)<<endl; //9找不到
cout<<search(arr,arr+len,iA.begin(),iA.end())<<endl;//返回地址位置,如果找不到返回负值地址信息
cout<<*search(arr,arr+len,iA.begin(),iA.end())<<endl;//获取值(查找子序列)
cout<<endl;
return 0;
}
从手写全排列到STL的实现
- C方式实现全排列
//用C的方式实现
#include<iostream>
using namespace std;
void swap(char* a,char* b)
{
char temp = *a;
*a = *b;
*b = temp;
}
void Permulation(char* pStr,char* pPostion)
{
//基准点
if(*pPostion == '\0')
{
cout<<pStr<<endl;
}else{
for(char* pChar = pBegin;*pChar != '\0';pChar++)
{
//依次和后面的字符交换
swap(*pChar,*pPostion);
Permulation(pStr,pPostion+1);
//换回来
swap(*pChar,*pPostion);
}
}
}
//输入123的全排列
int main()
{
char test[] = "123";
Permulation(test,test);
cout<<endl;
return 0;
}
- STL实现全排列
#include<algorithm>
#include<iostream>
using namespace std;
int main()
{
//用STL输出全排列
//注意:next_permutation必须要保证数组顺序,保证数组从小到大
char test[] = "123";
do{
cout<<test[0]<<test[1]<<test[2]<<endl;
}while(next_permutation(test,test+3));
//注意:prev_permutation必须要保证数组顺序,保证数组从大到小
char test2[] = "321";
do{
cout<<test2[0]<<test2[1]<<test2[2]<<endl;
}while(prev_permutation(test,test+3));
return 0;
}
迭代器基本使用
迭代器(iterator)
- 是一种smart pointer,用于访问顺序容器和关联容器中的元素,相当于容器和操纵容器的算法之间的中介;
- 迭代器按照定义方式分成以下四种:
- 正向迭代器:
iterator
; - 常量正向迭代器:
const_iterator
; - 反向迭代器:
reverse_iterator
; - 常量反向迭代器:
const_reverse_iterator
容器 | 迭代器功能 |
---|---|
vector | 随机访问 |
deque | 随机访问 |
list | 双向访问 |
set/multiset | 双向访问 |
map/multimap | 双向访问 |
stack | 不支持迭代器 |
queue | 不支持迭代器 |
priority_queue | 不支持迭代器 |
#include<list>
#include<iostream>
using namespace std;
int main()
{
list<int> v;
v.push_back(3);
v.push_back(4);
v.push_front(2);
v.push_front(1); //1,2,3,4
list<int>::const_iterator it;
for(it = v.begin();it != v.end();it++)
{
cout<<*it<<" ";
}
cout<<endl;
cout<<v.front()<<endl;
v.pop_front(); //弹出1
list<int>::reverse_iterator it2;
for(it2 = v.rbegin();it2 !=v.rend();it2++) //rbegin从尾部开始
{
*it2 += 1;
cout<<*it2<<" ";
}
return 0;
}
手写GC与异常
注:尽量少用异常处理,会影响程序的正常运行。
#include <iostream>
using namespace std;
int main()
{
double m ,n;
cin >> m >> n;
try {
cout << "before dividing." << endl;
if( n == 0)
throw -1; //抛出int类型异常
else
cout << m / n << endl;
cout << "after dividing." << endl;
}
catch(double d) {
cout << "catch(double) " << d << endl;
}
catch(int e) {
cout << "catch(int) " << e << endl;
}
cout << "finished" << endl;
return 0;
}
容器适配器
- stack 堆栈:
一种"先进后出"的容器,底层数据结构是使用的deque; - queue 队列:
一种"先进先出"的容器,底层数据结构是使用的deque; - priority_queue优先队列:
一种特殊的队列,它能够在队列中进行排序(堆排序),底层实现结构是vector或者deque;
#include<functional>
#include<stack>
#include<queue>
#include<iostream>
using namespace std;
int main()
{
priority_queue<int> pq;//默认是最大值优先
priority_queue<int,vector<int>,less<int> > pq2;//默认是最大值优先
priority_queue<int,vector<int>,greater<int> > pq3;//最小值优先
pq.push(2);
pq.push(1);
pq.push(3);
pq.push(0);
while(!pq.empty())
{
int top = pq.top();
cout<<"top is:"<<top<<endl;
pq.pop();
}
cout<<endl; // 3 2 1 0
pq2.push(2);
pq2.push(1);
pq2.push(3);
pq2.push(0);
while(!pq2.empty())
{
int top = pq2.top();
cout<<"top is:"<<top<<endl;
pq2.pop();
}
cout<<endl;// 3 2 1 0
pq3.push(2);
pq3.push(1);
pq3.push(3);
pq3.push(0);
while(!pq3.empty())
{
int top = pq3.top();
cout<<"top is:"<<top<<endl;
pq3.pop();
}
cout<<endl;// 0 1 2 3
return 0;
}
空间配置器初步
- 《STL源码剖析》侯捷,SGI STL版本的可读性较强;
- 从使用的角度来看,allocator隐藏在其他组件中默默工作,不需要关心,但是从理解STL实现角度来看,它是需要首先分析的组件;
- allocator的分析可以体现C++在性能和资源管理上优化思路;
jjalloc
经典版本,还是存在很多问题。
#pragma once
#ifndef JJALLOC
#define JJALLOC
#include<new>
#include<cstddef>
#include<climits>
#include<iostream>
namespace JJ
{
template <class T>
inline T* _allocate(ptrdiff_t size,T*)
{
set_new_handler(0);//告诉系统有些异常我们怎么处理,传入0是以前处理异常的时候全部清空(不太理解)
T* tmp = (T*)(::operator new((size_t)size * sizeof(T)));
if(tmp == 0)
{
cerr<<"out of memory"<<endl;
exit(1);
}
return tmp;
}
template<class T>
inline void _deallocate(T* buffer)
{
::operator delete(buffer);
}
template<class T1,class T2>
inline void _construct(T1* p,const T2& value)
{
new(p) T1(value);//placement new,invoke constuctor of T1
}
template<class T>
inline void _destroy(T* ptr)
{
ptr->~T();
}
template<class T>
class allocator{
public:
typedef T value_type;
typedef T* pointe;
typedef const T* const_pointer;
typedef T& reference;
typedef const T& const_reference;
typedef size_t size_type;
typedef ptrdiff_t difference_type;
//rebind allocator of type U
template<class U>
struct rebind{
typedef allocator<U> other;
};
/
//需要加上以下2个函数,windows的编译器用到了allocator不同类型的拷贝
allocator()
{
return;
}
template <class U>
allocator(const allocator<U>& c)
{
}
/
//hint user for locality,第2个参数是个提示,实际上可能会利用它来增进区域性
pointer allocate(size_type n,const void* hint = 0)
{
return _allocate(difference_type)n,(pointer)0);
}
void deallocate(pointer p,size_type n)
{
_deallocate(p);
}
void construct(pointer p,const T& value)
{
_construct()p,value);
}
void destroy(pointer p)
{
_destroy(p);
}
pointer address(reference x)(return (poiter)&x;}
const_pointer const_address(const_reference x)(return (const_pointer)&x;)
size_type max_size() const{
return size_type(UINT_MAX/sizeof(T));
}
};
}//#end of
#endif
main
#include "jjalloc.h"
#include<vector>
#include<iostream>
using namespace std;
int main()
{
int ia[5] = {
0,1,2,3,4};
unsigned int i;
vector<int,JJ::allocator<int> > iv
string s = lexical_cal<string>(1.23);
cout<<s<<endl;
}
自定义空间配置器
看不懂┗( ▔, ▔ )┛
STL空间配置器源码剖析与STL总结
STL总结
- STK 的六大组件给软件编程带来了新的多态复用,是现代C++语言高效的精髓;
- 泛型和STL的学习路线很陡,建议初学者先学会基本使用和简单扩展;
- 掌握了一定基础的情况下,可以通过进一步学习和分析源码,编写自己的组件来提升能力;
关于Boost库
- Boost库是为C++语言标准库提供扩展的一些C++程序库的总称,由Boost社区组织开发、维护,Boost库可以与C++标准库完美共同工作,并且为其提供扩展功能;
- Boost可为大致为20多个分类:
字符串和文本处理库,容器库,算法库,函数对象和高阶编程库,综合类库等等;
具体见:
https://www.boost.org/
https://dl.bintray.com/boostorg/release/
#include<boost/lexical_cast.hpp>
#include<iostream>
using namespace std;
using boost::lexical_cast;
int main()
{
int i = lexical_cast<int>("123");
cout<<i<<endl;
string s = lexical_cal<string>(1.23);
cout<<s<<endl;
}
CPP多线程基础
- C++ 11中的Thread使用;
- mutex等锁的使用;
- 进程与线程,同步与异步;
- 线程的交换与移动;
注:id是线程的唯一标识。
#include<thread>
#include<mutex> /排他锁
#include<iostream>
using namespace std;
mutex g_mutex;
void T1()
{
g_mutex.lock();
cout<< "T1 Hello"<<endl;
g_mutex.unlock();
}
void T2(const char* str)
{
g_mutex.lock();
cout<< "T2 "<<str<<endl;
g_mutex.unlock();
}
int main()
{
thread t1(T1);
thread t2(T2,"Hello World");
t1.join();//主线程保证子线程完成后再接着执行
t2.join();
cout<<"Main Hi"<<endl;
return 0;
}
多线程银行存取款案例
#include<iostream>
#include<thread>
#include<mutex>
using namespace std;
//存钱
void Deposit(mutex& m,int& money)
{
//锁的粒度尽可能最小化
for(int index = 0;index < 100;index++)
{
m.lock();
money += 1;
m.unlock();
}
}
//取钱
void Withdraw(mutex& m,int& money)
{
//锁的粒度尽可能最小化(包含在改变的变量中)
for(int index = 0;index < 100;index++)
{
m.lock();
money -= 1;
m.unlock();
}
}
int main()
{
int money = 2000;
mutex m;
cout<<"Current money is:"<<money<<endl; //2000
thread t1(Deposit,ref(m),ref(money)); //m不能直接传引用,需要使用ref
thread t1(Withdraw,ref(m),ref(money));
t1.join();
t2.join();
cout<<"Finally money is:"<<money<<endl; //2000
}
线程移动
#include<iostream>
#include<thread>
#include<mutex>
using namespace std;
int main()
{
//线程交换
thread tW1([](){
cout<<"ThreadSwap1"<<endl;
});
thread tW2([](){
cout<<"ThreadSwap2"<<endl;
});
cout<<"ThreadSwap1 id is"<<tW1.get_id()<<endl;
cout<<"ThreadSwap2 id is"<<tW2.get_id()<<endl;
cout<<"Swap after:"<<endl;
swap(tW1,tW2); //线程交换(句柄之间发生了交换)
cout<<"ThreadSwap1 id is"<<tW1.get_id()<<endl;
cout<<"ThreadSwap2 id is"<<tW2.get_id()<<endl;
t1.join();
t2.join();
//线程移动
thread.tM1([](){
cout<<"ThreadSwap1"<<endl;});
//tM1.join();
cout<<"ThreadSwap1 id is"<<tM1.get_id()<<endl;
cout<<"Swap after"<<endl;
thread tM2 = move(tM1);
cout<<"ThreadSwap2 id is"<<tM2.get_id()<<endl;
tM2.join();
return 0;
}
多线程小结
- C++11中Thread的使用;
- mutex等锁的使用;
- 进程与线程,同步与异步;
- 线程的交换与移动;
注:这方面知识介绍较少,但是尤其重要,之后需要花时间认真学学。