C++:STL是啥玩意?

C++标准模板库:STL(Standard Template Library)概述

C++中两个方面体现重用:
1. OOP:继承与多态,标准类库
2. 泛型程序设计(Generic Programming):模板,标准模板库STL
STL:就是一些常用的数据结构算法的模板的合集

1. 基本概念

  1. 容器、迭代器、算法
容器迭代器算法
可容纳各种数据类型的通用数据结构可用于依次存取容器中的元素用来操作容器中的元素的方法
类模板类似指针函数模板

算法:本身与他们操作的数据的类型无关,可以在从简单的数组到复杂容器的任何数据结构上使用,如:sort()对一个vector中数据排序、find()查找list中的某个对象…

  • 实例:int array[100];
    该数组就是容器,而int *类型的指针变量就可以作为迭代器,sort算法可以作用在该容器上,对其进行排序:
sort(array, array + 70);
//将前70个元素排序
//array、array+70都是迭代器

2. 容器

存放各种类型的数据(基本类型的变量,对象等)的通用数据结构

2.1 容器分类

顺序容器vertor(变长数组)、deque(双向队列)、list(双向列表)
关联容器setmultisetmapmultimap
容器适配器stack(栈)、queue(队列)、priority_queue(优先级队列)

2.2 容器概述

  1. 对象被插入容器中时,被插入的是对象的一个复制品。
  2. 放入容器的对象所属的类,往往应重载==<运算符(1.许多算法,要对容器内元素进行比较; 2.一些容器本身就是排序的,插入时要先进行比较)

2.3 顺序容器

  1. 容器并非排序的
  2. 元素的插入位置同元素的值无关
  3. vectordequelist
  1. vector: 头文件<vector>
    - 动态数组
    - 元素在内存连续存放
    - 支持随机存取
    - 尾部增删元素具有较好的性能(一般O(1),只有重新分配存储空间时才为O(n)_把原内容拷贝到新空间),但头部或中间增删为O(n)
  2. deque: 头文件<deque>
    - 双向队列
    - 元素在内存连续存放
    - 支持随机存取(次于vector)
    - 头尾增删元素具有较好性能(一般O(1))——队头指针、队尾指针
  3. list:头文件list
    - 双向链表
    - 不连续存放
    - 不支持随机存取
    - 任何位置增删元素都O(1) (不包括查找元素位置的时间)

2.4 关联容器

  1. 元素是排序的
  2. 插入任何元素,都按相应的排序规则来确定其位置
  3. 便于查找
  4. 通常以平衡二叉树方式实现,插入和检索时间都是O(log(N))
  5. set/ multisetmap/ multimap
  1. set / multiset:头文件<set>
    - set即集合
    - set不允许有相同元素
    - multiset中允许有相同
  2. map / multimap:头文件<map>
    - map与set的区别在于,map里只能放对象,该对象有且仅有两个成员变量,一个名为first(关键字),一个second(值),map根据first值对元素进行从"小"到"大"排序,并可以通过first来快速检索元素
    - map不允许有相同元素
    - multimap允许

2.5 容器适配器

  1. stack:头文件<stack>
    - 栈
    - 后进先出
  2. queue:头文件<queue>
    - 单向队列
    - 插入在尾部,删除、检索、修改只允许在头部进行
    - 先进先出
  3. priority_queue:头文件<queue>
    - 优先级队列
    - 最高优先级元素总是第一个出队(优先级自己定义)

2.6 成员函数

  1. 顺序容器和关联容器中都有的成员函数
成员函数功能
begin返回指向容器中第一个元素的迭代器
end返回指向容器中最后一个元素后面的位置的迭代器
rbegin返回指向容器中最后一个元素的迭代器
rend返回指向容器中第一个元素前面的位置的迭代器
erase从容器中删除一个或几个元素
clear从容器中删除所有元素
  1. 顺序容器中的成员函数
成员函数功能
front返回容器中第一个元素的引用
back返回容器中最后一个元素的引用
push_back在容器末尾增加新元素
pop_back删除容器末尾的元素
erase删除迭代器指向的元素(可能使该迭代器失效),或删除一个区间,返回被删除元素后面那个元素的迭代器

3. 迭代器

  1. 用于指向顺序容器和关联容器中的元素
  2. 迭代器用法和指针类似
  3. 有 const 和 非const 两种
  4. 通过迭代器可以读取它指向的元素
  5. 通过 非const 迭代器还可以修改其指向的元素

3.1 迭代器的使用方法

  1. 定义一个容器类(从容器模板实例化出来的类)的迭代器的方法有:
① 容器类名::iterator 变量名;
② 容器类名::const_iterator 变量名;
  • 访问一个迭代器指向的元素:
* 迭代器变量名
  • ++操作:迭代器执行++操作,使其指向容器中的下一个元素
    TIPS:如果迭代器到达了容器中的最后一个元素的后面,此时再使用它,就会出错,类似于使用NULL或未初始化的指针一样
  1. 实例
#include <vector>
#include <iostream>
using namespace std;

int main(){
	vector<int> v;	//一个存放int元素的数组,一开始里面没有元素
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	v.push_back(4);
	vector<int>::const_iterator i;	//常量迭代器(只能访问,不能修改)
	for(i =v.begin(); i != v.end(); ++i)
		cout << *i << ",";
	cout << endl;
	vector<int>::reverse_iterator r;	//反向迭代器,++后指向前一个元素
	for(r = v.rbegin(); r != v.rend(); ++r)
		cout << * r << ",";
	cout << endl;
	vector<int>::iterator j;	//非常量迭代器,可修改 
	for(j = v.begin(); j != v.end(); ++j)
		*j = 100;
	for(i = v.begin(); i != v.end(); ++i)
		cout << * i << ",";
	return 0;
} 

3.2 双向迭代器、随机访问迭代器

1)双向迭代器:pp1
++pp++:使p指向容器中下一个元素
--pp--:使p指向容器中上一个元素
* p:取p指向的元素
p = p1:赋值
p == p1p != p1:判断是否相等、不等

2)随机访问迭代器:pp1
◆双向迭代器的所有操作
p += i:将p向后移动i个元素
p -= i:将p向前移动i个元素
p + i值为:指向p后面第i个元素的迭代器
p - i值为:指向p前面第i个元素的迭代器
p[i]值为:p后面的第i个元素的引用
p < p1, p <= p1, p > p1, p >= p1

3)

容器容器上的迭代器类别
vector随机访问
deque随机访问
list双向
set/ multiset双向
map/multimap双向
stack不支持迭代器
queue不支持迭代器
priority_queue不支持迭代器

TIPS:有的算法需要随机访问迭代器,如:sort、binary_search…

3.3 遍历方式

  • 遍历vector的几种做法(deque同理):vector的迭代器是随机迭代器
vector<int> v(100);
int i;
for(i=0; i<v.size(); i++)
	cout << v[i];	//根据下标随机访问
vector<int>::const_iterator ii;
for(ii = v.begin(); ii != v.end(); ii++)
	cout << *ii;
for(ii = v.begin(); ii < v.end(); ii++)
//随机迭代器可以用 < 比较
	cout << *ii;
//间隔一个输出:
ii = v.begin();
while(ii < v.end()){
	cout << *ii;
	ii = ii + 2;
}
  • 遍历list的几种做法:list是双向迭代器
list<int> v;
list<int>::const_iterator ii;
for(ii = v.begin(); ii != v.end(); ++ii)
//双向迭代器只能用!=比较,且List没有[]成员函数,故不能用v[i]
	cout << *ii;
	

4. 算法简介

  1. 算法就是一个个函数模板,大多再<algorithm>中定义
  2. STL中提供能在各种容器中通用的算法,比如查找,排序等
  3. 算法通过迭代器来操作容器中的元素。许多算法可以对容器中的一个局部区间进行操作,因此需要2个参数,一个是起始元素的迭代器,一个是终止元素的后面一个元素的迭代器(比如:排序和查找)
  4. 有的算法返回一个迭代器(比如find()算法)
  5. 算法可以处理容器,也可以处理普通数组

4.1 算法实例:find()

template<class InIt, class T>
InIt find(InIt first, InIt last, const T& val);

◆first 和 last 这两个参数都是容器的迭代器,它们给出了容器中的查找区间起点和终点[first, last)。区间的起点是位于查找范围之中的,而终点不是。find()在[first, last)查找等于val的元素
◆用 == 运算符判断相等
◆函数返回值是一个迭代器。如果找到指向被找到元素,找不到,该迭代器等于last

  • 实例:find()使用:
#include <vector>
#include <algorithm>
#include <iostream>
using namespace std;

int main(){
	int array[10] = {10, 20, 30, 40};
	vector<int> v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	v.push_back(4);
	vector<int>::iterator p;
	p = find(v.begin(), v.end(), 3);
	if(p != v.end())
		cout << *p <<endl;		//输出3	 
	p = find(v.begin(), v.end(), 9);
	if(p == v.end())
		cout << "Not Found" <<endl;
	p = find(v.begin() + 1, v.end() - 2, 1);
	//整个容器:[1, 2, 3, 4],查找区间:[2,3),有效元素:2 
	if(p != v.end())
		cout << *p <<endl;
	int *pp = find(array, array+4, 20);		//数组名是迭代器
	cout << * pp <<endl; 
	
	return 0;
} 

5. 函数对象

若一个类重载了运算符(),则该类的对象就是函数对象
函数对象是对函数的一种集成,如实例2,不需要单独写2次方,3次方和…的函数,只需要一个函数对象SumPowers(…)即可

  • 实例1
#include <iostream>
using namespace std;

class CMyAverage{
	public:
		double operator () (int a1, int a2, int a3){
			return (double)(a1 + a2 + a3) / 3;
		}
};

int main(){
	CMyAverage average;
	cout << average(3, 2, 3);
	//等价于:average.operator()(3, 2, 3); 
	return 0;
} 
  • TIPS:一般在函数名或函数指针后面带参数 fun(...),average是个对象,但是接(参数),看上去就像函数调用,所以叫函数对象

  • 实例2

#include <iostream>
#include <vector>
#include <algorithm>
#include <numeric>
#include <functional>
using namespace std;

int SumSquares(int total, int value){
	return total + value * value;
}

template<class T>
void PrintInterval(T first, T last){
//输出区间[first, last)中的元素
	for(; first != last; ++first)
		cout << * first << " ";
	cout <<endl;
}

template<class T>
class SumPowers{
	private:
		int power;
	public:
		SumPowers(int p):power(p){}
		const T operator () (const T & total, const T & value){
		//计算value的power次方,加到total上去
			T v = value;
			for(int i=0; i < power - 1; ++i) 
				v = v * value;
			return total + v;
		}
};

int main(){
	const int SIZE = 10;
	int a1[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
	vector<int> v(a1, a1 + SIZE);
	cout << "1) "; PrintInterval(v.begin(), v.end());
	int result = accumulate(v.begin(), v.end(), 0, SumSquares);
	cout << "2) 平方和: " << result <<endl;
	result =
		accumulate(v.begin(), v.end(), 0, SumPowers<int>(3));	//SumPowers<int>(3) 函数对象 
	cout << "3) 3次方和: " << result <<endl;
	result =
		accumulate(v.begin(), v.end(), 0, SumPowers<int>(4));
	cout << "4) 4次方和: " << result;
	
	return 0; 
}

其中

accumulate(v.begin(),v.end(),0,SumSquares)
实例化出:参数一一对应
int accumulate(vector<int>::iterator first, vector<int>::iterator last, int init, int (* op)(int, int){
	for(; first != last; ++first)
		init = op(init, * first);
	return init;
}

TIPS:SumSquares实例化的参数为int (* op)(int, int)函数指针

accumulate(v.begin(), v.end(), 0, SumPowers<int>(3));
实例化出:
int accumulate(vector<int>::iterator first, vector<int>::iterator last, int init, SumPowers<int> op){
	for(; first != last; ++first)
		init = op(init, *first);
	return init;
}
  1. STL中的函数对象类模板: 头文件<functional> 如:equal_to、greater、less…
  • 实例:greater函数对象类模板
template<class T>
struct greater:public binary_function<T, T, bool>{
	bool operator () (const T& x, const T& y){
		return x < y;
	}
};

  • 实例:greater的应用
    list自带的sort:一种通过<升序排列,另一种自定义规则:将list中的元素按op规定的比较方法升序排列,即x<y 等价于 op(x, y)返回true
template<class Compare>
void sort(Compare op);
  • 实例:
#include <iostream>
#include <list>
using namespace std; 

class MyLess{
//比较个位数,谁个位小谁小 
	public:
		bool operator () (const int& c1, const int& c2){
			return (c1 % 10) < (c2 % 10);
		}
};

template<class T>
void Print(T first, T last){
	for(; first != last; ++first)
		cout << *first << ",";
}

int main(){
	const int SIZE = 5;
	int a[SIZE] = {5, 15, 7, 3, 11};
	list<int> lst(a, a+SIZE);
	lst.sort(MyLess());		//按MyLess()的规则来sort 
	//以函数对象作参数传入,相当于传入函数指针 
	Print(lst.begin(),lst.end());
	cout << endl;
	lst.sort(greater<int>());	//greater<int>()是个对象
	Print(lst.begin(), lst.end());
	cout << endl; 
	return 0; 
}

6. 补充

  1. STL中的“大”、“小”的概念:
    ◆关联容器内部元素都是从小到大排序的
    ◆有些算法要求其操作的区间是从小到大排序的,称为“有序区间算法”:binary_search
    ◆有些算法会对区间进行从小到大排序,称为“排序算法”:sort
    ◆在使用STL时,缺省情况下,一下三个说法等价:
    1)x比y小
    2)y比x大
    3)表达式x < y为true

  2. STL中的“相等”的概念
    ◆有时,"x和y相等"等价于"x == y"为true,例:在未排序的区间上进行的算法,如顺序查找find…
    ◆有时,"x和y相等"等价于"x小于y和y小于x"同时为false,例:有序区间算法,如binary_search、关联容器自身的成员函数find…

  • 实例:
#include <algorithm>
#include <iostream>
using namespace std;

class A{
	int v;
	public:
		A(int n):v(n){}
		bool operator < (const A & a2) const{
			cout << v << "v" << a2.v << "?" <<endl;
			return false;
		}
		bool operator == (const A & a2) const{
			cout << v << "==" << a2.v << "?" <<endl;
			return v == a2.v;
		}
};


int main(){
	A a[] = {A(1), A(2), A(3), A(4), A(5)};
	cout << binary_search(a, a + 4, A(9));	//折半查找 
	return 0;
} 
  • 输出结果:
3v9?
2v9?
1v9?
9v1?
1
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值