C++进阶编程

STL标准库简介

STL(Standard Template LIbrary)

  • STL算法是泛型的(generic),不与任何特定数据结构和对象绑定,不必在环境类似的情况下重写代码;
  • STL算法可以量身定做,并且具有很高的效率;
  • STL可以进行扩充,你可以编写自己的组件并且能与STL标准的组件进行很好的配合;
    在这里插入图片描述

序列容器和函数对象基本使用

容器

  • 容器用于存放数据;STL的容器分为两大类:
  1. 序列式容器(Sequence Containers):
    其中的元素都是可排序的(ordered),STL提供了vector、list、deque等序列式容器,而stack、queue、priority_queue则是容器适配器;
  2. 关联式容器(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>
  1. 非可变序列算法:指不直接修改其所操作的容器内容的算法;
  2. 可变序列算法:指可以修改它们所操作的容器内容的算法;
  3. 排序算法:包括对序列进行排序和合并的算法、搜索算法以及有序序列上的集合操作;
  4. 数值算法:对容器内容进行数值计算;
  • 最常见的算法包括:
    查找,排序和通用算法,排列组合算法,数值算法,集合算法等算法;
#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,用于访问顺序容器和关联容器中的元素,相当于容器和操纵容器的算法之间的中介;
  • 迭代器按照定义方式分成以下四种:
  1. 正向迭代器:iterator
  2. 常量正向迭代器:const_iterator
  3. 反向迭代器:reverse_iterator
  4. 常量反向迭代器: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等锁的使用;
  • 进程与线程,同步与异步;
  • 线程的交换与移动;

注:这方面知识介绍较少,但是尤其重要,之后需要花时间认真学学。

猜你喜欢

转载自blog.csdn.net/XZ2585458279/article/details/109759664