教程 - 软件 - 文章 - 论坛

::白话C++::

更多感受-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++课程, 您能实现吗?

先来看看有多少事情要做吧!

  1. 必须实现成绩录入;
  2. 必须实现对录入的成绩进行排序;
  3. 必须实现将排序后的成绩输出。

行吗?试试看吧。

3.1 成绩排序程序V1.0

新建一个控制台应用工程。刚从Hello WindowsHello 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,但程序已经是我们所要的结果了。


作业:

  1. 将Student结构,更改成如下:

struct Student

{

    string name;

    int score1;

    int score2;

};

即,原来的单一成绩,改成有两门功课的成绩。然后有以下要求: ①输入时,输入姓名和两个成绩;②排序时,按两个成绩的总分进行排序;③输出时,显示两个成绩及相加的总分。请实现。

版权所有 谢绝复制。作者:南郁(nanyu) www.d2school.com