更多感受-Hello STL
STL:Standard Template
Library/标准模板库。C++标准程序库的重要部分。 程序库是什么?
想像一个七夕的夜晚,初恋的小丁准备做“一件浪漫的事”:送给女友99朵玫瑰,然后争取和女友完成一个甜蜜的初吻。
小丁和女友的初吻有没完成?这不是我们课程关心的事。我们要关心的是,这件事情中,小丁需要调用哪些外在的服务才能完成。
首先是鲜花,营造浪漫气氛的重要工具。小丁是不是在开春时就在家里开始种玫瑰呢?或许亲手种的花更显诚意,但我们相信小丁会选择从花店购买。
小丁穷,没私家车;女友家不远不近,如何去女友家楼下?他选择了打车。
在楼下,小丁拿出手机约女友下来——等下,移动或联通公司想不想在本课程此处插播广告?中国电信也莫放弃,我可以让小丁在课程里,坚持只用小灵通。还有手机厂商——不管如何,小丁
此时确实需要信号良好的无线通讯服务。
仅此而已吗?事实上,小丁那天身上穿的,头上戴的……
在这些小丁使用的服务中,无论是培植鲜花、交通工具、也无论是通讯、打扮什么的;小丁如果自己去实现的话,都比较困难,就算
勉强实现了,效果也不见得比使用专业的服务好。

(小丁的初吻)
写程序也如此。假设我们要实现某程序,主要目标是A。因为现实中,A目标可能没有现成的服务,或者虽然有,但性价比不高,不划算。因此这个主要目的我们自己来实现。通常,我们称这个目的为“业务逻辑”。但是在实现这个业务逻辑的过程中,我们可能
得先实现子逻辑B、C、D、E等。而B、C、D、E这几个功能,说不定就有现成的程序库可以辅助实现。
STL就是这样一套程序库。程序库为我们提供可复用的代码。包括函数、数据、数据结构、类库、框架等等。
有个同学要提问——我知道,可怜的孩子,他连初恋都未曾有过。他问:“那么七夕节当晚的活动中,小丁的业务逻辑呢是什么”?
仔细想想,什么事情是明明没有专业公司做得好,但又非小丁完成不可呢?当然是:和女友亲嘴!初吻的版本是1.0。之前或许还有0.7版:对着墙壁;0.8版:对着镜子;0.9版:强抓了一位男同事……可以推论1.0版肯定做得不怎样,但记住了:关键的业务逻辑,一定要掌握在自己手心。
言归正传,让我们来快速感受一下STL。
第3小节:Hello STL!
有个小学老师拜访,希望让您写个程序,为班上的考试成绩排序。他承诺不会公开排行榜(听说是教育部有规定)。刚刚学了第2学堂的一点点C++课程,
您能实现吗?
先来看看有多少事情要做吧!
- 必须实现成绩录入;
- 必须实现对录入的成绩进行排序;
- 必须实现将排序后的成绩输出。
行吗?试试看吧。 3.1 成绩排序程序V1.0
新建一个控制台应用工程。刚从Hello Windows
或 Hello Internet 过来,要是忘了如何创建控制台工程。请复习第一小节Hello
World 。 编辑含有main函数的代码文件,最终内容为:
//---------------------------------------------------------------------------
#pragma hdrstop
#include <string>
#include <cstdlib>
#include <fstream>
#include <iostream>
#include <iterators> //如果是Turbo C++ 请改为: #include <iterator>
#include <list>
#include <algorithm>
#include <functional>
//---------------------------------------------------------------------------
using namespace std;
#pragma argsused
int main(int argc, char* argv[])
{
list<int> ss;
cout << "请输入要排序的数字" << endl
<< "(每个数字之间以空格或换行分隔)" << endl
<< "(按F6键,并回车结束输入): " << endl;
//录入成绩:
copy(istream_iterator<int>(cin), istream_iterator<int>(), back_inserter(ss));
//排序(从高分到低分):
ss.sort(greater<int>());
//输出排序后的成绩:
copy(ss.begin(), ss.end(), ostream_iterator<int>(cout, "\n"));
system("pause");
return 0;
}
//---------------------------------------------------------------------------
|
(成绩排序程序V1.0代码)
| 代码中,cout <<
"请输入要排序的数字"……用于提示如何输入。共有三行,但其实只需一行硬折成三行,目的为了查看方便。结果是,仅在第3行最后以分号(;)结束,用于表示一行代码结束。前面两“行”行末没有,也不能有分号。 |
保存,编译,运行,我随意输入一些成绩(这个程序不支持带0.5分的成绩,要实现其实很容易,留在我们以后在学习数据类型的作业里了),结果截图如下:

(成绩排序第1版运行结果)
| 截图中的“^Z”,并不是我输入了这两个字符,而是按F6键(F6呢,当然也不是F和6这两个键),然后回车的屏幕结果。Windows的控制台程序,采用F6键来表示一段屏幕输入的结束。 |
关于STL,这第一版的排序程序给了我们什么感受呢?
有关C++,或许你在听说它强大的同时,也听过有关它的很多“谣言”。比如有关指针、内存分配、释放是如何如何的容易出错。在本例,我们肯定需一段内存来保存录入的成绩,但我们事先并不知道成绩个数,这就一定要用到“动态内存分配”。STL提供了各种各样内存容器,为我们解决了动态内存分配与释放的相关问题;所以在例子中,我们看不到有关内存分配和释放的代码。
其它还有:输入输出流的操作、一个强大的copy和流迭代器的互操作,甚至把逻辑上本应有的循环操作,都隐藏了。最后我们关心的排序操作,在sort算法和greater函数对象天衣无缝般的合作中实现。
算法?流?迭代器?函数对象?这些是什么?作为初学者,现在不了解这些概念,不必着急,毕竟这只是一节“感受”课。还是让我们继续感受STL吧。
3.2 成绩排序程序 V2.0
1.0版中,没有提供学员的名字,这样的输出感觉不太直观;因此,是时候为那位小学老师提供2.0版了。
建议另建一新工程,然后把1.0版的代码全部复制过来。要知道,在STL的世界里,1.0所展示的是如同Hello
World的经典代码,值得珍藏。 2.0版的完整代码我并不完全列出,仅指出需要增加或改动的地方。
首先是增加的代码。在原代码的“using namespace std;”行之后,“#pragma argsused”行之前,插入以下新增的代码:
//定义一个包括姓名和成绩的学生结构:
struct Student
{
string name;
int score;
};
//定义如何判断两个学生谁的成绩高:
bool operator > (Student const& s1, Student const& s2)
{
return (s1.score > s2.score);
}
//定义如何输入一个学生:
istream& operator >> (istream& is, Student& s)
{
return (is >> s.name >> s.score);
}
//定义如何输出一个学生:
ostream& operator << (ostream& os, Student const& s)
{
os << s.name << ", " << s.score;
return os;
}
|
(新增有关学生结构的代码)
| 输入本段代码时,请特别注意,struct Student {...};
右括号之后,尾随一个分号,不要遗漏了;否则,程序编译时可能会报出很多错误。 |
然后,将main函数内(不包括main函数的参数定义那一行)的所有int内,修改为Student。熟练的话,你可以选择那段代码,然后用C++
Builder 主菜单下Edit内的查找替换命令,在2秒之内完成。然后修改有关输入提示的那段话。 修改之一:“int”
替换为 “Student”;修改之二:“请输入要排序学生姓名及成绩……”。
int main(int argc, char* argv[])
{
list<Student> ss;
cout << "请输入要排序学生姓名及成绩" << endl
<< "(先输入姓名,再输入成绩;用空格或换行分隔。)" << endl
<< "(按F6键,并回车结束输入): " << endl;
//录入成绩:
copy(istream_iterator<Student>(cin), istream_iterator<Student>(), back_inserter(ss));
//排序(从高分到低分):
ss.sort(greater<Student>());
//输出排序后的成绩:
copy(ss.begin(), ss.end(), ostream_iterator<Student>(cout, "\n"));
system("pause");
return 0;
}
|
(main函数体内的修改结果)
保存、编译、运行。下面是我的运行结果。

(成绩排序第2版运行结果)
我们新定义了一个结构:Student,用于包容成绩和姓名,然后提供为这个结构提供涉及“输入、排序、输出”所需要的函数。而我们原来和业务相关的程序流程及框架,全都没有改变,仅是套入的元素,由int变成Student,但程序已经是我们所要的结果了。
作业:
- 将Student结构,更改成如下:
struct Student
{
string name;
int score1;
int score2;
};
即,原来的单一成绩,改成有两门功课的成绩。然后有以下要求:
①输入时,输入姓名和两个成绩;②排序时,按两个成绩的总分进行排序;③输出时,显示两个成绩及相加的总分。请实现。 |