STL与泛型编程入门
Iterator(迭代器) Container(容器) Algorithm(算法) Adaptors(配接器)
抽象的重要性
–计算机科学的重要进步,许多是由于发掘了新的抽象性质而促成的
–面向过程->基于对象->面向对象->泛型
面向过程(Procedure-Oriented)的抽象
–抽象出Procedure(Function)的概念,把程序分成若干个子过程。将事物的
方法隐藏于各个函数内--C语言。
–适用于处理小型的程序。对大型程序, 子程序之间关系复杂,不易处理
变化的需求--引发软件危机的原因--需要新的抽象。
示例:
#include <stdio.h>
void fun()
{
// fun() 隐藏了一系列方法
.
.
.
.
.
}
int main()
{
fun();
return 0;
}
基于对象(Object-Based)的抽象
–引入抽象数据类型(ADT,Abstract DataType)。C++的类,将事物的
属性与
方法紧密地结合在一起--VB、带类的C。
–与面向过程相比,可以更好地处理变化,一定程度上化解了软件危机。但各个类之间的关系不容易处理,而且程序
代码数量比面向过程时更大--需要新的抽象。
示例:
#include<iostream>
using namespace std;
class A<span style="white-space:pre"> </span>//方法与属性的紧密结合
{
int x;
public :
A(int x=0):x(x){}
void pxy()
{
cout<<x<<endl;
}
};
int main()
{
A *p =new A(2) ;
p->pxy();
delete p;
return 0;
}
面向对象(Object-Oriented)的抽象
–抽象出
封装、继承、多态( polymorphic)的概念。
–与基于对象相比,有更多的间接性。运用多态,我们可以
调用某种方法,而不用指定此方法所属的类型。因而达到更进一步的抽象性。
示例:
#include <iostream>
#include <cmath>
using namespace std;
#define pai acos(-1);
class Shape{
public:
Shape(){}
~Shape(){}
virtual float GetArea(){return -1;}
};
class retc :virtual public Shape
{
int w,h;
public:
retc(int w,int h):w(w),h(h){ cout<<"Creatre rect\n"; }
~retc(){cout<<"Delete rect\n";}
float GetArea(){return w*h;}
};
class Circ :virtual public Shape
{
int R;
public:
Circ(int r): R(r){ cout<<"Creatre Circle\n"; }
~Circ(){cout<<"Delete Circle\n";}
float GetArea();
};
float Circ::GetArea()
{
float ans=3.1415926*R*R;
return ans;
}
class Square : public retc
{
public:
Square(int w):retc(w,w)
{
cout<<"Creatre Square\n";
}
~Square()
{
cout<<"Delete Square\n";
}
};
int main()
{
Shape *ptr;
Circ circ(2);
retc ret(3,4);
Square square(3);
<span style="white-space:pre"> </span>ptr = ˆ //对用户封装了具体的类型,用户只需和抽象类打交道
<span style="white-space:pre"> </span>cout<<endl<<ptr->GetArea()<<endl;
<span style="white-space:pre"> </span>ptr = &ret;
<span style="white-space:pre"> </span>cout<<endl<<ptr->GetArea()<<endl;
<span style="white-space:pre"> </span>ptr = □
<span style="white-space:pre"> </span>cout<<endl<<ptr->GetArea()<<endl;
return 0;
}
STL 的概念:
何为STL?
–STL(StandardTemplate Library)是C++标准庫的一部分(80%),是用C++Template机制来表达泛型的庫。
–
STL(StandardTemplate Library)是用泛型技术来设计完成的实例
vector <int> a; //定义一个动态数组
deque <int> b; //定义一个双端队列
.
.
.
sort(a.begin(),a.end()); //用一个泛型算法可以处理多种数据结构。
//而且在获得弹性的同时运行效率上和以前相比没有损失。
sort(b.begin(),b.end());
STL
六大组件
–容器(Container)
–算法(Algorithm)
–迭代器(Iterator)
–仿函数(Function object)
–适配器(Adaptor)
–空间配制器(allocator)
#include<iostream>
#include<algorithm>
#include<list>
#include<functional>
using namespace std;
typedef int elemtype;
template <typename T>
void printelem (T elem)
{
cout<<elem<<" ;";
}
void (*pe)(elemtype) = printelem; //函数指针
int main()
{
int a[5]={1,2,3,4,5};
list<elemtype> li(a,a+5); //用a数组做list容器的初始值
for_each(li.begin(),li.end(),pe); //对区间内每个元素调用传入的操作pe
cout<<endl;
li.push_back(6);
li.push_back(7);
li.push_back(0);
li.push_back(9);
for_each(li.begin(),li.end(),pe);
cout<<endl;
li.remove_if(bind2nd(modulus<elemtype>(),2)); //除去所有奇数
for_each(li.begin(),li.end(),pe);
cout<<endl;
return 0;
}
template //函数模板
本文介绍函数模板的概念、用途以及如何创建函数模板和函数模板的使用方法......
在创建完成抽象操作的函数时,如:拷贝,反转和排序,你必须定义多个版本以便能处理每一种
数据类型。以 max() 函数为例,它返回两个参数中的较大者:double max(double first, double second);
complex max(complex first, complex second);
date max(date first, date second);
//..该函数的其它版本
尽管这个函数针对不同的
数据类型其实现都是一样的,但程序员必须为每一种数据类型定义一个单独的版本:
double max(double first, double second)
{
return first>second? first : second;
}
complex max(complex first, complex second)
{
return first>second? first : second;
}
date max(date first, date second)
{
return first>second? first : second;
}
用普通函数来实现抽象操作会迫使你定义多个函数实例,从而招致不小的维护工作和调试开销。解决办法是使用函数模板代替普通函数。
函数声明格式
template <class(或
typename) any(或任意符合规则的名称)
>(如果还有其他类型名,就用逗号隔开)
返回类型 函数名(
形参
表);
函数定义和声明格式基本相同,形参表要加上形参名,分号改为函数体。
声明的例子:
template <class type1, class type2>
type1 add(type1,type2);
template <class type1, class type2>
type1 add(type1 a,type2 b)
{return a + (type1)b;}
也可以直接定义函数,不声明。
#include <iostream>
using std::cout;
using std::endl;
//声明一个函数模版,用来比较输入的两个相同数据类型的参数的大小,class也可以被typename代替,
//T可以被名称字代替,只要符合命名规则即可。
template <class T>
T min(T& x,T& y)
{ return(x<y)?x:y;}
int main( )
{
int n1 = 2,n2 = 10;
double d1 = 1.5,d2 = 5.6;
cout<< "较小整数:"<<min(n1,n2)<<endl;
cout<< "较小实数:"<<min(d1,d2)<<endl;
system("PAUSE");
return 0;
}
程序运行结果:
程序分析:main()函数中定义了两个
整型变量n1 , n2 两个双精度类型变量d1 , d2然后调用min( n1, n2); 即实例化
函数模板T min(T x, T y)其中T为int型,求出n1,n2中的最小值.同理调用min(d1,d2)时,求出d1,d2中的最小值.
Container(容器)的概念:
–用来管理一组元素。
容器的分类
–序列式容器(Sequencecontainers)
每个元素都有固定位置--取决于插入时机和地点,和元素值无关。
vector、deque、list
–关联式容器(Associatedcontainers)
元素位置取决于特定的排序准则,和插入顺序无关
set、multiset、map、multimap
Vectors
–将元素置于一个动态数组中加以管理。
–
–可以随机存取元素(用索引直接存取)。
–
–数组尾部添加或移除元素非常快速。但是在中部或头部安插元素比较费时。
Deques
–deque,是“double-ended queue”的缩写。
–
–可以随机存取元素(用索引直接存取)。
–
–数组头部和尾部添加或移除元素都非常快速。但是在中部或头部安插元素比较费时。
Lists
–双向链表。
–
–不提供随机存取(按顺序走到需存取的元素,O(n))。
–
–在任何位置上执行插入或删除动作都非常迅速,内部只需调整一下指针。
迭代器(Iterator)
指针与数组
const int size=6;
int a[size]={0,1,2,3,4,5};
int *begin=a;
int *end=a+6;
for(int *p=begin;p!=end;p++)
{
cout<<*p<<" ";
}
cout<<endl;
指针与其它数据结构呢?比如说链表?
–存储空间是非连续的。不能通过对指向这种数据结构的指针做累加来遍历。
–能不能提供一个行为类似指针的类,来对非数组的数据结构进行遍历呢?这样我们就能够以同样的方式来遍历所有的数据结构(所有容器)。
迭代器与容器
–通过迭代器,我们可以
用相同的方式来访问、遍历(泛型抽象)容器。
迭代器的概念
–迭代器是一个“可遍历STL容器内全部或部分元素”的对象。
–
–一个迭代器指出容器中的一个特定位置。
–
具有遍历复杂数据结构的能力
迭代器的作用
–能够让迭代器与算法不干扰的相互发展,最后又能无间隙的粘合起来。
–
–重载了*,++,==,!=,=运算符。用以操作复杂的数据结构。
–
–容器提供迭代器,算法使用迭代器。
迭代器的分类:
一个例子:
template<class Input_iterator,class Distance>
void advance_II(Input_iterator & i, Distance n)
{
for(;n>0;n--,i++); //单项逐一前进
}
template<class Bidirectional_iterator,class Distance>
void advance_BI(Bidirectional_iterator & i, Distance n)
{
if(n>=0) //双项逐一前进
for(;n>0;n--,i++);
else
for(;n<0;n++,i--);
}
template<class RandomAcess_iterator,class Distance>
void advance_RAI(RandomAcess_iterator & i, Distance n)
{
i+=n; //随机,跳跃前进
}
Algorithm(算法)
泛型算法通则
–所有算法的前两个参数都是一对iterators:[first,last),用来指出容器内一个范围内的元素。
–每个算法的声明中,都表现出它所需要的最低层次的iterator类型。
–大部分算法都可以用functioinobject 来更改准则。functionobject又称functor。
–
不修改内容的序列操作:
adjacent_find | 查找两个相邻(Adjacent)的等价(Identical)元素 |
all_ofC++11 | 检测在给定范围中是否所有元素都满足给定的条件 |
any_ofC++11 | 检测在给定范围中是否存在元素满足给定条件 |
count | 返回值等价于给定值的元素的个数 |
count_if | 返回值满足给定条件的元素的个数 |
equal | 返回两个范围是否相等 |
find | 返回第一个值等价于给定值的元素 |
find_end | 查找范围A中与范围B等价的子范围最后出现的位置 |
find_first_of | 查找范围A中第一个与范围B中任一元素等价的元素的位置 |
find_if | 返回第一个值满足给定条件的元素 |
find_if_notC++11 | 返回第一个值不满足给定条件的元素 |
for_each | 对范围中的每个元素调用指定函数 |
mismatch | 返回两个范围中第一个元素不等价的位置 |
none_ofC++11 | 检测在给定范围中是否不存在元素满足给定的条件 |
search | 在范围A中查找第一个与范围B等价的子范围的位置 |
search_n | 在给定范围中查找第一个连续n个元素都等价于给定值的子范围的位置 |
修改内容的序列操作:
copy | 将一个范围中的元素拷贝到新的位置处 |
copy_backward | 将一个范围中的元素按逆序拷贝到新的位置处 |
copy_ifC++11 | 将一个范围中满足给定条件的元素拷贝到新的位置处 |
copy_nC++11 | 拷贝 n 个元素到新的位置处 |
fill | 将一个范围的元素赋值为给定值 |
fill_n | 将某个位置开始的 n 个元素赋值为给定值 |
generate | 将一个函数的执行结果保存到指定范围的元素中,用于批量赋值范围中的元素 |
generate_n | 将一个函数的执行结果保存到指定位置开始的 n 个元素中 |
iter_swap | 交换两个迭代器(Iterator)指向的元素 |
moveC++11 | 将一个范围中的元素移动到新的位置处 |
move_backwardC++11 | 将一个范围中的元素按逆序移动到新的位置处 |
random_shuffle | 随机打乱指定范围中的元素的位置 |
remove | 将一个范围中值等价于给定值的元素删除 |
remove_if | 将一个范围中值满足给定条件的元素删除 |
remove_copy | 拷贝一个范围的元素,将其中值等价于给定值的元素删除 |
remove_copy_if | 拷贝一个范围的元素,将其中值满足给定条件的元素删除 |
replace | 将一个范围中值等价于给定值的元素赋值为新的值 |
replace_copy | 拷贝一个范围的元素,将其中值等价于给定值的元素赋值为新的值 |
replace_copy_if | 拷贝一个范围的元素,将其中值满足给定条件的元素赋值为新的值 |
replace_if | 将一个范围中值满足给定条件的元素赋值为新的值 |
reverse | 反转排序指定范围中的元素 |
reverse_copy | 拷贝指定范围的反转排序结果 |
rotate | 循环移动指定范围中的元素 |
rotate_copy | 拷贝指定范围的循环移动结果 |
shuffleC++11 | 用指定的随机数引擎随机打乱指定范围中的元素的位置 |
swap | 交换两个对象的值 |
swap_ranges | 交换两个范围的元素 |
transform | 对指定范围中的每个元素调用某个函数以改变元素的值 |
unique | 删除指定范围中的所有连续重复元素,仅仅留下每组等值元素中的第一个元素。 |
unique_copy | 拷贝指定范围的唯一化(参考上述的 unique)结果 |
划分操作:
is_partitionedC++11 | 检测某个范围是否按指定谓词(Predicate)划分过 |
partition | 将某个范围划分为两组 |
partition_copyC++11 | 拷贝指定范围的划分结果 |
partition_pointC++11 | 返回被划分范围的划分点 |
stable_partition | 稳定划分,两组元素各维持相对顺序 |
排序操作:
is_sortedC++11 | 检测指定范围是否已排序 |
is_sorted_untilC++11 | 返回最大已排序子范围 |
nth_element | 部份排序指定范围中的元素,使得范围按给定位置处的元素划分 |
partial_sort | 部份排序 |
partial_sort_copy | 拷贝部分排序的结果 |
sort | 排序 |
stable_sort | 稳定排序 |
二分法查找操作:
binary_search | 判断范围中是否存在值等价于给定值的元素 |
equal_range | 返回范围中值等于给定值的元素组成的子范围 |
lower_bound | 返回指向范围中第一个值大于或等于给定值的元素的迭代器 |
upper_bound | 返回指向范围中第一个值大于给定值的元素的迭代器 |
集合操作:
includes | 判断一个集合是否是另一个集合的子集 |
inplace_merge | 就绪合并 |
merge | 合并 |
set_difference | 获得两个集合的差集 |
set_intersection | 获得两个集合的交集 |
set_symmetric_difference | 获得两个集合的对称差 |
set_union | 获得两个集合的并集 |
堆操作:
is_heap | 检测给定范围是否满足堆结构 |
is_heap_untilC++11 | 检测给定范围中满足堆结构的最大子范围 |
make_heap | 用给定范围构造出一个堆 |
pop_heap | 从一个堆中删除最大的元素 |
push_heap | 向堆中增加一个元素 |
sort_heap | 将满足堆结构的范围排序 |
最大/最小操作:
is_permutationC++11 | 判断一个序列是否是另一个序列的一种排序 |
lexicographical_compare | 比较两个序列的字典序 |
max | 返回两个元素中值最大的元素 |
max_element | 返回给定范围中值最大的元素 |
min | 返回两个元素中值最小的元素 |
min_element | 返回给定范围中值最小的元素 |
minmaxC++11 | 返回两个元素中值最大及最小的元素 |
minmax_elementC++11 | 返回给定范围中值最大及最小的元素 |
next_permutation | 返回给定范围中的元素组成的下一个按字典序的排列 |
prev_permutation | 返回给定范围中的元素组成的上一个按字典序的排列 |