STL 简介
1. STL 是什么
作为一个C++ 程序设计者,STL 是一种不可忽视的技术。
Standard Template Library (STL) :标准模板库, 更准确的说是 C++ 程序设计语言标准模板库。STL 是所有C++ 编译器和所有操作系统平台都支持的一种库,说它是一种库是因为,虽然STL 是一种标准,也就是说对所有的编译器来说,提供给C++ 程序设计者的接口都是一样的。也就是说同一段STL 代码在不同编译器和操作系统平台上运行的结果都是相同的,但是底层实现可以是不同的。 令人兴奋的是,STL 的使用者并不需要了解它的底层实现。 试想一下,如果我们有一把能打开所有锁的钥匙,那将是多么令人疯狂啊。
STL 的目的是标准化组件,这样你就不用重新开发它们了。你可以仅仅使用这些现成的组件。STL 现在是C++ 的一部分,因此不用额外安装什么。它被内建在你的编译器之内。
2. 为什么我们需要学习 STL
STL 是 C++ 的ANSI/ISO 标准的一部分, 可以用于所有C++ 语言编译器和所有平台(Windows/Unix/Linux..) 。STL 的同一版本在任意硬件配置下都是可用的;
STL 提供了大量的可复用软件组织。例如,程序员再也不用自己设计排序,搜索算法了,这些都已经是STL 的一部分了。嘎嘎,有意思吧。
使用STL 的应用程序保证了得到的实现在处理速度和内存利用方面都是高效的,因为STL 设计者们已经为我们考虑好了。
使用STL 编写的代码更容易修改和阅读,这是当然的啦。因为代码更短了,很多基础工作代码已经被组件化了;
使用简单,虽然内部实现很复杂。
虽然,STL 的优点甚多,但是STL 的语法实在令初学者人头疼,许多人望而却步。可是STL 是每个C++ 程序设计者迟早都要啃的一块骨头。
3. 初识 STL
下面让我们来看几段代码吧:
#include <iostream>
int main(void)
{
double a[] = {1, 2, 3, 4, 5};
std::cout<<mean(a, 5)<<std::endl; // will print 3
return 0;
}
好懂吧,除了那个std 有点让人不舒服以外,这是一段普通的没有使用STL 的C++ 代码。
再看下面一段:
#include <vector>
#include <iostream>
int main()
{
std::vector<double> a;
a.push_back(1);
a.push_back(2);
a.push_back(3);
a.push_back(4);
a.push_back(5);
for(int i = 0; i < a.size(); ++i)
{
std::cout<<a[i]<<std::endl;
}
return 0;
}
如果你真的没有接触过STL 的话,你会问,呀, vector 是啥呀?这是一段纯种的STL 代码,看到尖括号了吧,知道那是模板了吧。看到 a.push_back(5) 、a.size() 你不感觉奇怪么?可是我们并没有定义这些函数啊。
#include <vector>
#include <iostream>
int main()
{
std::vector< int > q;
q.push_back(10);
q.push_back(11);
q.push_back(12);
std::vector< int > v;
for(int i=0; i<5; ++i){
v.push_back(i);
}
std::vector<int>::iterator it = v.begin() + 1;
it = v.insert(it, 33);
v.insert(it, q.begin(), q.end());
it = v.begin() + 3;
v.insert(it, 3, -1);
it = v.begin() + 4;
v.erase(it);
it = v.begin() + 1;
v.erase(it, it + 4);
v.clear();
return 0;
}
这一段你又看到了新东西了吧: iterator 、insert 、erase 、clear 。不罗嗦了,等你看完这篇文章,回头再看就简单了。
关于模板的其他细节,读者可以参阅《C++ Templates 中文版》在这里,简单的介绍一下模板类和函数模板的概念。
模板是C++ 中实现代码重用机制的一种工具,可以实现类型参数化,把类型定义为参数。函数模板和类模板允许用户构造模板函数和模板类。
图 1
下面我们来看一段函数模板的例子:
#include<iostream>
#include<string>
using namespace std;
// 定义函数模板
template<class T> //template 是关键字,T 表示一种待实例化的类型
//template<typename T> 也是对的
T MAX(T a, T b)// 函数模板,函数名为 max ,此函数有2 个T 类型的参数,返回类型为T
{
return (a>b)?a:b;
}
// 在此例实例化的时候,T 可以是多种类型的,int,char,string …
int main()
{
int x=2,y=6;
double x1=9.123,y1=12.6543;
cout<<" 把T 实例化为int:"<<MAX(x,y)<<endl;// 实例化函数模板,把T 实例化为int
cout<<" 把T 实例化为double:"<<MAX(x1,y1)<<endl; // 把T 实例化为double
}
下面再看看,类模板:
#include<iostream>
using namespace std;
// 定义名为ex_class 的类模板
template < typename T> class ex_class
{
T value;
public:
ex_class(T v) { value=v; }
void set_value(T v) { value=v; }
T get_value(void) {return value;}
};
//main() 函数中测试ex_class 类模板
int main()
{
// 测试int 类型数据
ex_class <int> a(5),b(10);
cout<<"a.value:"<<a.get_value()<<endl;
cout<<"b.value:"<<b.get_value()<<endl;
// 测试char 类型数据
ex_class <char> ch('A');
cout<<"ch.value:"<<ch.get_value()<<endl;
ch.set_value('a');
cout<<"ch.value:"<<ch.get_value()<<endl;
// 测试double 类型数据
ex_class <double> x(5.5);
cout<<"x.value:"<<x.get_value()<<endl;
x.set_value(7.5);
cout<<"x.value:"<<x.get_value()<<endl;
}
4. STL 的组成
STL 有三大核心部分:容器(Container )、算法(Algorithms )、迭代器(Iterator ),容器适配器(container adaptor ),函数对象(functor) ,除此之外还有STL 其他标准组件。通俗的讲:
容器:装东西的东西,装水的杯子,装咸水的大海,装人的教室……STL 里的容器是可容纳一些数据的模板类。
算法:就是往杯子里倒水,往大海里排污,从教室里撵人……STL 里的算法,就是处理容器里面数据的方法、操作。
迭代器:往杯子里倒水的水壶,排污的管道,撵人的那个物业管理人员……STL 里的迭代器:遍历容器中数据的对象。对存储于容器中的数据进行处理时,迭代器能从一个成员移向另一个成员。他能按预先定义的顺序在某些容器中的成员间移动。对普通的一维数组、向量、双端队列和列表来说,迭代器是一种指针。
下面让我们来看看专家是怎么说的:
容器(container ):容器是数据在内存中组织的方法,例如,数组、堆栈、队列、链表或二叉树(不过这些都不是STL 标准容器)。STL 中的容器是一种存储T (Template )类型值的有限集合的数据结构, 容器的内部实现一般是类。这些值可以是对象本身,如果数据类型T 代表的是Class 的话。
算法(algorithm ):算法是应用在容器上以各种方法处理其内容的行为或功能。例如,有对容器内容排序、复制、检索和合并的算法。在STL 中,算法是由模板函数表现的。这些函数不是容器类的成员函数。相反,它们是独立的函数。令人吃惊的特点之一就是其算法如此通用。不仅可以将其用于STL 容器,而且可以用于普通的C ++数组或任何其他应用程序指定的容器。
迭代器(iterator) :一旦选定一种容器类型和数据行为( 算法) ,那么剩下唯一要他做的就是用迭代器使其相互作用。可以把达代器看作一个指向容器中元素的普通指针。可以如递增一个指针那样递增迭代器,使其依次指向容器中每一个后继的元素。迭代器是STL 的一个关键部分,因为它将算法和容器连在一起。
下面我将依次介绍STL 的这三个主要组件。
1. 容器
STL 中的容器有队列容器和关联容器,容器适配器(congtainer adapters :stack,queue ,priority queue ),位集(bit_set ),串包(string_package) 等等。
在本文中,我将介绍list,vector ,deque 等队列容器,和set 和multisets,map 和multimaps 等关联容器,一共7 种基本容器类。
队列容器(顺序容器):队列容器按照线性排列来存储T 类型值的集合,队列的每个成员都有自己的特有的位置。顺序容器有向量类型、双端队列类型、列表类型三种。
u 基本容器—— 向量
向量(vector 容器类): #include <vector> , vector 是一种动态数组,是基本数组的类模板。其内部定义了很多基本操作。既然这是一个类,那么它就会有自己的构造函数。vector 类中定义了4 中种构造函数:
· 默认构造函数,构造一个初始长度为0 的空向量,如: vector<int> v1 ;
· 带有单个整形参数的构造函数,此参数描述了向量的初始大小。这个构造函数还有一个可选的参数,这是一个类型为T 的实例,描述了各个向量种各成员的初始值;如: vector<int> v2(n,0 ); 如果预先定义了: n, 他的成员值都被初始化为0 ;