本文面向
读者:学习过C 程序设计语言(也就是说学习过Template),但是还没有接触过STL
STL
初学者。这实际上是我学习STL
一篇笔记,老鸟就不用看了。 字串5
什么是泛型程序设计 字串8
我们可以简单
理解为:使用模板
程序设计就是泛型程序设计。就像我们我们可以简单
理解面向对象程序设计就是使用虚函数
程序设计一样。 字串6
STL是什么 字串9
作为一个C 程序设计者,STL是一种不可忽视
技术。Sandard Template Library (STL): 字串1
标准模板库,更准确
说是 C 程序设计语言标准模板库。学习过MFC
人知道,MFC是微软公司创建
C 类库。而与之类似
是 STL 是模板库,只不过 STL 是 ANSI/ISO 标准
一部分,而 MFC 只不过是微软
一个产品而已。也就是说STL是所有C 编译器和所有操作系统平台都支持
一种库,说它是一种库是因为,虽然STL是一种标准,也就是说对所有
编译器来说,提供给C 程序设计者
接口都是一样
。也就是说同一段STL代码在不同编译器和操作系统平台上运行
结果都是相同
,但是底层实现可以是不同
。 令人兴奋
是,STL
使用者并不需要了解它
底层实现。 试想一下,如果我们有一把能打开所有锁
钥匙,那将是多么令人疯狂啊。嘎嘎。这个歪梦我做了20多年鸟。 字串1
STL
目
是标准化组件,这样你就不用重新开发它们了。你可以仅仅使用这些现成
组件。STL现在是C
一部分,因此不用额外安装什么。它被内建在你
编译器之内。 字串8
为什么我们需要学习STL
ANSI/ISO 标准
一部分,可以用于所有C 语言编译器和所有平台(Windows/Unix/Linux..)。STL
同一版本在任意硬件配置下都是可用
;
可复用软件组织。例如,程序员再也不用自己设计排序,搜索算法了,这些都已经是STL
一部分了。嘎嘎,有意思吧;
应用程序保证了得到
实现在处理速度和内存利用方面都是高效
,因为STL设计者们已经为我们考虑
了;
代码更容易修改和阅读,这是当然
鸟。因为代码更短了,很多基础工作代码已经被组件化了; 虽然,STL
优点甚多,但是STL
语法实在令初学者人头疼,许多人望而却步。可是STL是每个C 程序设计者迟早都要啃
一块骨头。因为越来越多
C 代码是用STL编写
,看不懂麻烦就大鸟。越来越多
人在用STL,不懂就无法和别人一起合作了。
事多磨嘛,早点学习早点解脱。 字串3
下面让我们来看几段代码吧:(你觉得头疼就不要看了) 字串8
//stl_cpp_1.cpp
#include
double mean(double *array, size_t n)
{
double m=0;
for(size_t i=0; i m = array[i];
}
return m/n;
}
int main(void)
{
double a[] = {1, 2, 3, 4, 5};
std::cout< return 0;
}
懂吧,除了那个std有点让人不舒服以外?这是一段普通
没有使用STL
C 代码。再看下面一段://stl_cpp_2.cpp
#include
#include
int main(void)
{
std::vector a;
std::vector::const_iterator i;
a.push_back(1);
a.push_back(2);
a.push_back(3);
a.push_back(4);
a.push_back(5);
for(i=a.begin(); i!=a.end(); i){
std::cout<<(*i)< }
return 0;
} 如果你真
没有接触过STL
话,你会问,呀,vector 是啥呀?我会告诉你,那是一排美女。嘎嘎。这可不是个比喻,表想歪鸟。这是一段纯种
STL代码,看到尖括号了吧,知道那是模板了吧。看到a.push_back(5),a.begin(),a.end()你不感觉奇怪么?可是我们并没有定义这些函数啊。//stl_cpp_3.cpp 字串1
#include
#include
int main(void)
{
std::vector q;
q.push_back(10);
q.push_back(11);
q.push_back(12);
std::vector v;
for(int i=0; i<5; i){
v.push_back(i);
}
std::vector::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???不罗嗦了,等你看完这篇文章,回头再看就简单了。在正式介绍STL之前,我们需要花点时间来了解一下模板和命名空间。 字串2
字串4
关于模板
其他细节,读者可以参阅《C Templates 中文版》(有点费脑子哦)。在这里,我只简单
介绍一下模板类和函数模板
概念。 字串1
模板是C 中实现代码重用机制
一种工具,可以实现类型参数化,把类型定义为参数。函数模板和类模板允许用户构造模板函数和模板类。 字串3

图1 字串1
下面我们来看一段函数模板
例子: 字串2
//stl_cpp_4.cpp
#include
#include
//定义函数模板
template //template 是关键字,T 表示一种待实例化
类型
//template 也是对
T max(T a, T b)//函数模板,函数名为 max,此函数有2个T类型
参数,返回类型为T
{
return (a>b)?a:b;
}
//在此例实例化
时候,T可以是多种类型
,int,char,string…
int main(void)
{
int x=2,y=6;
double x1=9.123,y1=12.6543;
cout<<"把T实例化为int:"< cout<<"把T实例化为double:"< //实例化函数模板,把T实例化为double
getchar(); //这一行代码用来在dos下查看结果,也可以用cin.get();
} 下面再看看,类模板://stl_cpp_5.cpp
#include
//定义名为ex_class
类模板 字串9
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(void)
{
//测试int类型数据
ex_class a(5),b(10);
cout<<"a.value:"< cout<<"b.value:"< //测试char类型数据
ex_class ch(''A'');
cout<<"ch.value:"< ch.set_value(''a'');
cout<<"ch.value:"< //测试double类型数据
ex_class x(5.5);
cout<<"x.value:"< x.set_value(7.5);
cout<<"x.value:"<} 命名空间(名字空间)
命名空间是C
一种机制,用来把单个标识符下
大量有逻辑联系
程序实体组合到一起。此标识符作为此组群
名字。命名空间用关键字namespace 来定义://stl_cpp_6.cpp 一个namespace是指一个具名
#include
using namespace std;
namespace printA
{
print() {cout<<"using namespace printA….."<
namespace printB
{
print() {cout<<"using namespace printB….."<
int main(void)
{
printA::print(); //测试命名空间printA, ::是作用域解析运算符
printB::print();
}
命名空间可以嵌套定义:
namespace A
{
functiong1(){};
namespace B
{ }
}
范围(named scope)。namespace被用来将相关
声明划归在一起,将不相关
代码部分隔开。命名空间只是命名了一个特殊
作用域,当程序很大,而且需要多人合作
时候,命名空间就显得特别
重要。比如2个程序员A,B 在同一个程序中定义了函数 pop(),如果没有使用命名空间,则会出错,而且这种错误难以检测出来。为了安全起见,他们可以定义不同
命名空间 A和B,在用
时候可以使用A::pop()和B::pop()来区分。 字串6
在STL中,标准库
全部成员在预先定义
命名空间std中。如果要用类模板vector ,有两种方法:一是在程序
前面添加预处理指令: #include 第二种方法是:
using namespace std; #include 动态绑定和静态绑定 字串5
using std::vector;
所谓绑定是指,对于参与多态行为
类型,他们具有多态行为
接口是在公共基类
设计中就预先确定
。而非绑定则对于参与多态行为
类型,他们
接口没有预先定义。 字串4
在C 中通过继承实现
多态是动态绑定,通过模板实现
多态是静态绑定。动态绑定
接口是在运行期间(动态)完成
,静态绑定
接口是在编译期间(静态)完成
。
了,有了以上
知识我们可以来学习STL 了。 字串2
STL
组成 字串9
STL有三大核心部分:容器(Container)、算法(Algorithms)、迭代器(Iterator),容器适配器(container adaptor),函数对象(functor),除此之外还有STL其他标准组件。
东西,装水
杯子,装咸水
大海,装人
教室……STL里
容器是可容纳一些数据
模板类;
算法,就是处理容器里面数据
方法,操作;
水壶,排污
管道,撵人
那个物业管理人员……STL里
迭代器:遍历容器中数据
对象; 对存储于容器中
数据进行处理时,迭代器能从一个成员移向另一个成员。他能按预先定义
顺序在某些容器中
成员间移动。对普通
一维数组、向量、双端队列和列表来说,迭代器是一种指针。 字串8
字串8
知道了吧?嘎嘎,当然了,你猜到了,那是我在瞎扯蛋。
下面让我们来看看专家是怎么说
:
字串1
方法,例如,数组、堆栈、队列、链表或二叉树(不过这些都不是STL标准容器)。STL中
容器是一种存储T(Template)类型值
有限集合
数据结构,容器
内部实现一般是类。这些值可以是对象本身,如果数据类型T代表
是Class
话。
行为或功能。例如,有对容器内容排序、复制、检索和合并
算法。在STL中,算法是由模板函数表现
。这些函数不是容器类
成员函数。相反,它们是独立
函数。令人吃惊
特点之一就是其算法如此通用。不仅可以将其用于STL容器,而且可以用于普通
C++数组或任何其他应用程序指定
容器。
就是用迭代器使其相互作用。可以把达代器看作一个指向容器中元素
普通指针。可以如递增一个指针那样递增迭代器,使其依次指向容器中每一个后继
元素。迭代器是STL
一个关键部分,因为它将算法和容器连在一起。 下面我将依次介绍STL
这三个主要组件。
字串1
容器
STL中
容器有队列容器和关联容器,容器适配器(congtainer adapters:stack,queue,priority queue),位集(bit_set),串包(string_package)等等。
字串1
在本文中,我将介绍list,vector,deque等队列容器,和set和multisets,map和multimaps等关联容器,一共7种基本容器类。 字串6
队列容器(顺序容器):队列容器按照线性排列来存储T类型值
集合,队列
每个成员都有自己
特有
位置。顺序容器有向量类型、双端队列类型、列表类型三种。
基本容器——顺序容器
字串6
向量(vector容器类):#include
类模板。其内部定义了很多基本操作。既然这是一个类,那么它就会有自己
构造函数。vector 类中定义了4中种构造函数: 字串5
空向量, 字串8 如:vector
构造函数,此参数描述了向量
初始大小。这个构造函数还有一个可选
参数,这是一个类型为T
实例,描述了各个向量种各成员
初始值; 如:vector
成员值都被初始化为0;
向量,作为已存在
向量
完全复制,
如:vector
构造函数,产生初始值为一个区间
向量。区间由一个半开区间[first,last](MS word
显示可能会有问题,first前是一个左方括号,last后面是一个右圆括号)来指定。 字串1 如:vector
下面一个例子用
是第四种构造方法,其它
方法读者可以自己试试。 字串9
//stl_cpp_7.cpp
//程序:初始化演示
#include
#include
#include
using namespace std;
int ar[10] = { 12, 45, 234, 64, 12, 35, 63, 23, 12, 55 };
char* str = "Hello World";
int main(void)
{
vector vec1(ar, ar 10); //first=ar,last=ar 10,不包括ar 10
vector vec2(str, str strlen(str)); //first=str,last= str strlen(str),不包括最后一个
cout<<"vec1:"<//打印vec1和vec2,const_iterator是迭代器,后面会讲到
//当然,也可以用for (int i=0; i//size()是vector
一个成员函数
for(vector::const_iterator p=vec1.begin();p!=vec1.end(); p)
cout<<*p;
cout<<''
''<<"vec2:"< for(vector::const_iterator p1=vec2.begin();p1!=vec2.end(); p1)
cout<<*p1;
getchar();
return 0;
} 为了帮助理解向量
概念,这里写了一个小例子,其中用到了vector
成员函数:begin(),end(),push_back(),assign(),front(),back(),erase(),empty(),at(),size()。//stl_cpp_8.cpp 字串1
#include
#include
using namespace std;
typedef vector INTVECTOR;//自定义类型INTVECTOR
//测试vector容器
功能
void main(void)
{
//vec1对象初始为空
INTVECTOR vec1;
//vec2对象最初有10个值为6
元素
INTVECTOR vec2(10,6);
//vec3对象最初有3个值为6
元素,拷贝构造
INTVECTOR vec3(vec2.begin(),vec2.begin() 3);
//声明一个名为i
双向迭代器
INTVECTOR::iterator i;
//从前向后显示vec1中
数据
cout<<"vec1.begin()--vec1.end():"< for (i =vec1.begin(); i !=vec1.end(); i)
cout << *i << " ";
cout << endl;
//从前向后显示vec2中
数据
cout<<"vec2.begin()--vec2.end():"< for (i =vec2.begin(); i !=vec2.end(); i) 字串2
cout << *i << " ";
cout << endl;
//从前向后显示vec3中
数据
cout<<"vec3.begin()--vec3.end():"< for (i =vec3.begin(); i !=vec3.end(); i)
cout << *i << " ";
cout << endl;
//测试添加和插入成员函数,vector不支持从前插入
vec1.push_back(2);//从后面添加一个成员
vec1.push_back(4);
vec1.insert(vec1.begin() 1,5);//在vec1第一个
位置上插入成员5
//从vec1第一
位置开始插入vec3
所有成员
vec1.insert(vec1.begin() 1,vec3.begin(),vec3.end());
cout<<"after push() and insert() now the vec1 is:" < for (i =vec1.begin(); i !=vec1.end(); i)
cout << *i << " ";
cout << endl;
//测试赋值成员函数
vec2.assign(8,1); // 重新给vec2赋值,8个成员
初始值都为1
cout<<"vec2.assign(8,1):" <字串4 ![我要研发网[www.51dev.com]](/templets/images/toplogo.gif)


