1.基本概念
STL(Standard Template Library,标准模板库)是惠普实验室开发的一系列软件的统称。虽然主要出现在C++中,但在被引入C++之前该技术就已经存在了很长的一段时间。
STL的从广义上讲分为三部分:algorithm(算法)、container(容器)和iterator(迭代器),容器和算法通过迭代器可以进行无缝地连接。几乎所有的代码都采 用了模板类和模板函数的方式,这相比于传统的由函数和类组成的库来说提供了更好的代码重用机会。在C++标准中,STL被组织为下面的13个头文件:
<algorithm>、<deque>、<functional>、<iterator>、<vector>、<list>、<map>、<memory>、<numeric>、<queue>、<set>、<stack> 和<utility>
此外,C++的STL还包括仿函数、适配器以及空间适配器。
2.STL的优势
- STL是C++的一部分,因此不用额外安装什么,它被内建在你的编译器之内。
- STL的一个重要特点是数据结构和算法的分离。尽管这是个简单的概念,但是这种分离确实使得STL变得非常通用。例如,在STL的vector容器中,可以放入元素(对象)、基础数据类型变量、指针;STL的sort()函数可以用来操作vector,list等容器。
- 程序员可以不用思考STL具体的实现过程,只要能够熟练使用STL就OK了。这样他们就可以把精力放在程序开发的别的方面。
- STL具有高可重用性,高性能,高移植性,跨平台的优点。
- STL有以下这些好处,我们知道STL无疑是最值得C++程序员骄傲的一部分。每一个C++程序员都应该好好学习STL。只有能够熟练使用STL的程序员,才是好的C++程序员。
高可重用性: STL中几乎所有的代码都采用了模板类和模版函数的方式实现,这相比于传统的由函数和类组成的库来说提供了更好的代码重用机会。
高性能:如map可以高效地从十万条记录里面查找出指定的记录,因为map是采用红黑树的变体实现的。(红黑树是平横二叉树的一种)
高移植性:如在项目A上用STL编写的模块,可以直接移植到项目B上。
跨平台:如用windows的Visual Studio编写的代码可以在Mac OS的XCode上直接编译。
3.容器
在实际的开发过程中,数据结构本身的重要性不会逊于操作于数据结构的算法的重要性,当程序中存在着对时间要求很高的部分时,数据结构的选择就显得更加重要。
经典的数据结构数量有限,但是我们常常重复着一些为了实现向量、链表等结构而编写的代码,这些代码都十分相似,只是为了适应不同数据的变化而在细节上有所出入。STL容器就为我们提供了这样的方便,它允许我们重复利用已有的代码实现构造自己的特定类型下的数据结构,通过设置一些模板,STL容器对最常用的数据结构提供了支持,这些模板的参数允许我们指定容器中元素的数据类型,可以将我们许多重复而乏味的工作简化。
在C++ 中容器被定义为:在数据存储上,有一种对象类型,它可以持有其它对象或指向其它对象的指针,这种对象类型就叫做容器。
容器部分主要由头文件<vector>,<list>,<deque>,<set>,<map>,<stack> 和<queue>
组成
容器的分类:
标准C++的STL框架中的容器主要有两大类:
序列式容器(Sequence containers): 将一组具有相同类型T的对象,以严格的线性形式组织在一起。
每个元素都有固定位置--取决于插入时机和地点,和元素值无关。如:
vector(向量)、deque(double-ended queue双端队列)、list(表)
序列容器可以视为数组和链表的推广。
关联式容器(Associated containers): 关联容器的特点是(键)有序的,即元素是按预定义的键顺序(如升序)插入的。
元素位置取决于特定的排序准则,和插入顺序无关 :
set<Key>(集合)multiset<Key>(多重集合)map<Key, T>(映射/映像)multimap<Key, T>(多重映射)hash_set<Key, H>(散列集)hash_map<Key, T, H>(散列映射)hash_multimap<Key, T, H>(散列多映射)
容器适配器(container adapter)不是独立的容器,只是某种容器的变种,它提供原容器的一个专用的受限接口。特别是,容器适配器不提供迭代器。
stack<T>(栈)queue<T>(队列)priority_queue<T>(优先队列)
4.迭代器
软件设计有一个基本原则,所有的问题都可以通过引进一个间接层来简化,这种简化在STL中就是用迭代器来完成的。概括来说,迭代器在STL中用来将算法和容器联系起来,起着一种黏和剂的作用。几乎STL提供的所有算法都是通过迭代器存取元素序列进行工作的,每一个容器都定义了其本身所专有的迭代器,用以存取容器中的元素。
迭代器部分主要由头文件<utility>,<iterator>和<memory>
组成。<utility>
是一个很小的头文件,它包括了贯穿使用在STL中的几个模板的声明,<iterator>
中提供了迭代器使用的许多方法,而对于<memory>
的描述则十分的困难,它以不同寻常的方式为容器中的元素分配存储空间,同时也为某些算法执行期间产生 的临时对象提供机制,<memory>
中的主要部分是模板类allocator,它负责产生所有容器中的默认分配器。
简单来说,迭代器就是一个指针,指向具体容器中的具体元素,解引用以后的数据类型就是容器元素的数据类型。是容器和算法的粘合剂,是一个间接层。
5.算法
函数库对数据类型的选择对其可重用性起着至关重要的作用。举例来说,一个求方根的函数,在使用浮点数作为其参数类型的情况下的可重用性肯定比使用整型作为它的参数类性要高。而C++通过模板的机制允许推迟对某些类型的选择,直到真正想使用模板或者说对模板进行特化的时候,STL就利用了这一点提 供了相当多的有用算法。它是在一个有效的框架中完成这些算法的——可以将所有的类型划分为少数的几类,然后就可以在模版的参数中使用一种类型替换掉同一种类中的其他类型。
STL提供了大约100个实现算法的模版函数,比如算法for_each将为指定序列中的每一个元素调用指定的函数,stable_sort以 你所指定的规则对序列进行稳定性排序等等。这样一来,只要熟悉了STL之后,许多代码可以被大大的化简,只需要通过调用一两个算法模板,就可以完成所需要 的功能并大大地提升效率。
算法部分主要由头文件,和组 成。是所有STL头文件中最大的一个(尽管它很好理解),它是由一大堆模版函数组成的,可以认为每个函数在很大程度上都是独立的,其中常用到的功能范围涉及到比较、交换、查找、遍历操作、复制、修改、移除、反转、排序、合并等等。体积很小,只包括几个在序列上面进行简单数学运算的模板函数,包括加法和乘法在序列上的一些操作。中则定义了一些模板类,用以声明函数对象。
说白了就是一系列的函数集合,这些函数通常是模板函数。
代码示例
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
void func1()
{
// 定义一个容器:用来存数据、存 int 数据
vector<int> a;
a.push_back(1);
a.push_back(2);
a.push_back(1);
a.push_back(3);
a.push_back(1);
a.push_back(4);
a.push_back(1);
a.push_back(5);
// 迭代器,提供了遍历容器的统一方法
//vector<int>::iterator it;
//for (it = a.begin(); it != a.end(); it++)
//{
// cout << *it << " ";
//}
//cout << endl;
vector<int>::iterator it = a.begin();
while (it != a.end())
{
cout << *it << " ";
it++;
}
cout << endl;
// 算法:用来处理容器中的数据,通过迭代器
int num = count(a.begin(), a.end(), 1);
cout << num << endl;
}
class Student
{
public:
Student (int id, int age)
{
this->id = id;
this->age = age;
}
void print()
{
printf ("id = %d, age = %d\n", id, age);
}
private:
int id;
int age;
};
void func2()
{
Student s1(10,20);
Student s2(11,21);
Student s3(12,22);
Student s4(13,23);
Student s5(14,24);
vector<Student> v;
v.push_back(s1);
v.push_back(s2);
v.push_back(s3);
v.push_back(s4);
v.push_back(s5);
vector<Student>::iterator it = v.begin();
while (it != v.end())
{
it->print();
it++;
}
}
void func3()
{
Student s1(10,20);
Student s2(11,21);
Student s3(12,22);
Student s4(13,23);
Student s5(14,24);
vector<Student*> v;
// v.push_back(&s1);
// v.push_back(&s2);
// v.push_back(&s3);
// v.push_back(&s4);
// v.push_back(&s5);
v.push_back(new Student(10,20));
v.push_back(new Student(11,21));
v.push_back(new Student(12,22));
v.push_back(new Student(13,23));
v.push_back(new Student(14,24));
vector<Student*>::iterator it = v.begin();
while (it != v.end())
{
(*it)->print();
it++;
}
}
int main()
{
// func1();
// func2();
func3();
return 0;
}