0. 视频
1. DRY 原则简介
上一节课,我们写了两版“问候”程序。第一版的最大问题是重复的内容比较多,每一次问候都在重复从 cout 到 endl 的代码,中间只有人名不同。
刚学编程的人可能会说:“代码重复怕什么啊?在电脑里 复制、 粘贴 文本非常快啊!”
问题不在于写代码是快还是慢,而在于重复的代码,难以修改、难以维护。
重复的代码,它们的背后的业务逻辑往往是相同或者高度相似的。如果有一天,这个业务逻辑发生改变,就会造成代码中所有重复的地方都要修改。我们现在的代码才几行而已,如果有一天q有上千,上万行代码,我们很可能都无法找出所有重复的地方,一个个修改过去也极大可能引发新的错误。
“不要到处写重复的代码”,这已经成为专门一条行业内的编码规矩,称为 “DRY”原则,也就是 “Dont’t repeat yourself”,不要重复你自己。
2. 新情景、新需求
第二版的问候程序,有很大的进步。因为我们把重复的,问候操作,写成一个函数,叫Hello(),多次调用。这样一来,将来,如果问候操作,需要变化,我们也只需要修改这个Hello()函数即可。
// 我的第一个函数
void Hello(std::string_view XXX)
{
std::cout << "你好," << XXX << "!" << std::endl;
}
这一节课,我们要为问候业务,提出新的场景需求:模拟在上班路上,碰到谁,就问候谁的过程。
碰上谁,就问候谁……那我们能事先知道每一天上班会碰到谁吗?当然无法预知。这样一看,现有的这第二版问候程序就显得非常“死板”了。每一次运行这个程序,都是先问候志玲,再问候幂幂,再问候冰冰……无论运行多少次,它的问候过程都是一成不变的。
这就说到了“变”!我们需要可以变的姓名;这就引出这节谭,们要学习第一个重要的知识点:变量。
3. 认识变量和常量
程序中的数据,可以按可变和不可变区分,分成两大类:常量和变量。
3.1 常量
常量,就是在程序运行之后,无法修改,或禁止修改它的“量”的数据。
这里的“量”,更通俗易懂的说法是一个数据的“值”,或者说,一块内存里存放的内容。
我们以后会重点讲解常量。不过,大家要在今天记住这两个描述:“无能力修改” 和 “禁止修改”。
来举一个“恶俗”,但绝对管用的例子吧——
假设你是一个非常喜欢挤别人痘痘的人。然后,有个女生说她后背某个地方长了一颗很大的痘痘,可是她不告诉你这颗痘痘的位置。请问,你能挤掉一颗你看不见,摸不着的痘痘吗?当然不能,这就叫“无能力修改”。
另外一种情况,这颗痘痘就长在女生鼻头上,你看得见,摸得着,但是你能伸手就挤掉它吗?也不能,因为人家女生不允许你动它。
这就是将来我们会学习到的,最常见的两种类型的常量:一种是代码在客观上没有能力修改的数据,一种在语法上明确标明不让修改的数据。上一课第一版代码中的名字字符串,就是第一种,叫“字面常量”。它就像我们白纸黑字签下的字,一旦写下,就涂改不了。
3.2 变量
初中学习的代数,当一个数据我们暂时不知道它会是什么值时,我们可以用个字母,比如 x、y、z,来暂时先代表这个数据。在程序中,一个数据,我们暂时不知道它会是什么值;或者,知道它一开始的值,但这个值会随着程序的运行而不断变化,这情况下,我们也可以用一个字母,或者更好的,用一个单词来表达它,表达这个会变化的数据。
今天,我们不知道什么?答,我们不知道每一天上班路上会先遇到谁,后遇到谁。所以,就让我们写的C++程序,定义第一个真正意义上的变量,取名为 “谁” 吧!
你当然也可以使用 X 来表达,这样显得更加有科技感和神秘感。但是,我觉得用名字是“谁”这个变量,来表达程序中可能遇到谁谁谁谁……想一想是不是也挺直观的?
没错,这也是我们今天学习到又一个重要的知识点:变量的名字一定要能直观地表达它的作用。
来看一些常量与变量使用对比的例子——
-
“我的体重是 60 公斤”:人的体重很少一成不变,所以我们得用变量表达它,这个变量的名字可以叫 weight。
-
“我的身高 175 厘米……”:也一样,从小到大再到老,身高也会变化,这个变量可以叫 height。
3.3 变量常见使用过程
结合用变量表达待问候的人名的例子,我们来看看变量最常见的使用过程。
(1)定义变量
要使用变量,首先需要定义变量。C++定义变量时,需要给出变量名字,还需要直接或间接给出变量的类型。今天我们学习其中的“直接”方式:
变量类型 变量名字;
即:先写下变量的类型,再写下它的名字,然后用分号结束,这是定义变量的一种常见语法。
在我们的例子,变量名是谁,它实际是要表达人的名字,所以它的类型应该是字符串。在 C++ 的标准库中,有一个叫 string 的类型,用来表达字符串。
std::string 谁; // 定义了一个变量,名字叫谁,类型是 std::string
大家还记得前面课堂学习到的名字空间的知识点吧?特别是这个 std:: 的写法,你应该很熟悉了。
这样定义的变量谁,一开始时,它并不持任何字符内容(或者说,它持有一个空的字符串)。
(2)修改变量的值
有了一个变量,接下来,就可以修改它的“量”了,也就是为它赋值。你可能猜到了,赋值操作在C++中,就是日常的 “=” 操作符(等号)。
下面演示了赋值 “张三” 给 谁 的代码:
谁 = "张三"; // 修改这个变量的值 (量、内存)
(3)使用变量
变量很方便用来传递一个值,比如,我们可以把 谁 作为之前我们写的 Hello() 函数的入参,以实现将 谁 的当前值(第2步中的 “张三”)传递给 Hello() 函数的入参(还记得吗,这个入参是一个“偷窥狂”,哦不是,是“观察者”)……
Hello(谁); // 将谁的当前值,传递给 Hello() 函数入参,再于函数内部输出其值
(4)再改,再用
变量变量,比孙悟空还善变,悟空才能连续72变,我们的变量可以无穷尽地变下去……
谁 = "李天王";
Hello(谁);
谁 = "孙悟空";
Hello(谁);
// ...
3.4 变量使用完整代码
#include <iostream>
#include <string>
#include <string_view>
void Hello(std::string_view XXX)
{
std::cout << "你好," << XXX << "!" << std::endl;
}
int main()
{
std::string 谁;
谁 = "志玲";
Hello(谁);
谁 = "幂幂";
Hello(谁);
谁 = "冰冰";
Hello(谁);
谁 = "热巴";
Hello(谁);
谁 = "真木阳子";
Hello(谁);
谁 = "井川遥";
Hello(谁);
谁 = "Lily Collins";
Hello(谁);
}
这个使用变量的版本,感觉写起来,比之前的常量版本还啰嗦呀?
4. 人机交互!
4.1 认识 getline()
尽管用了变量,但这个版本每次运行起来,输出结果还是一成不变;甚至写起来比常量的那个版本,还要麻烦、还啰嗦,并且,没有引入什么新功能呀?!
我们准备这样来模拟每天上班路上遇到谁问候谁的过程:我们让用户(其实,就是我们自己),在程序运行时,通过键盘输入一个姓名,输入谁,就表示我们遇到谁。
我们要输入的是一个姓名,一个字符串。并且,如果是外国人的话,那么他们的姓名中间可能包含空格。C++标准库提供了一个 getline() 的函数,特别适合于读入整行的字符串,包括中间的空格。
getline() 有两个入参。第一个入参指定输入流。什么叫“输入流”?
【输入、输出】:首先来理解什么叫“输入”:往程序里“塞”数据,就叫“输入”。大家顺便可以理解一下什么叫 “输出”,程序往外“吐”数据,就叫输出,也就是,编程时讲的输入和输出,是以程序为中心来理解的。
【流】:那什么叫流:水流、电流,都是流。流就是在同一个方向上的排着队的一大堆数据。比如,“车流”就是:同一个行驶方向上,有很多车一辆接一辆地往前走。
本站深入讲解,包含多个实践项目的 C++“流”的高级课程。
《C++“流”编程视频辅导》课程 建议及时加深学习
我们现在写的程序,暂时只能使用键盘往程序里输入数据,一阵“噼噼啪啪”敲键盘,就可以往程序里输入一大串字符,因此它满足“输入流”的概念的三个要素:
- 同一个方向:键盘到主机
- 支持连续:噼噼啪啪快速输入,一个字符都不会丢
- 支持排队(即有序):无论你击键有多少,都不可能出现先输入字符的后到,后输入的字符先到的乱序情况
所以,C++标准库就把键盘输入,包装成一个 标准字符输入流,并且预先定义成一个变量。
如果是我写的标准库,那这个变量的名字肯定得叫 噼噼啪啪,可惜不是。
这个预先定义的,代表标准输入流,或者说,绝大多数情况下,代表键盘的变量,它的真正的名字叫 cin。c指字符,in 指输入 。加上标准库名字空间直接限定,就是 std::cin。
getline() 函数的第二个入参,在本例中,我们给的是我们自己定义的字符串变量,也就是谁。
当程序这样写时:
getline(std::cin, 谁);
执行该句代码并完成 getline() 的调用之后,变量谁的值,就是用户从键盘上输入的一行文本。输入一行文本,是指用户输入一些普通字符内容(包括汉字内容)之后,非常潇洒的拍下键盘上的回车键。getline()开始从行首一直读到行尾,不含回车换行。
如果用户在此期间就是不碰键盘,那程序就会“卡”在这一行一动不动。
4.2 人机交互第一版
#include <iostream>
#include <string_view>
#include <string>
void Hello(std::string_view XXX)
{
std::cout << "你好," << XXX << "!" << std::endl;
}
int main()
{
std::string 谁;
std::getline(std::cin, 谁);
Hello(谁);
std::getline(std::cin, 谁);
Hello(谁);
}
为了不浪费版面,暂时就只支持上班路上遇到两个吧!后面的遇到的,管他是大老板还是小保安,管她是不是你暗恋的办公室女神,咱们就全都假装没看到。
5. 综合版
将 “遇见” 的过程,也封装成一个函数:Input(),该函数返回用户输入的姓名,并且提供了更好的人机交互界面。
#include <iostream>
#include <string_view>
#include <string>
// 负责问候(输出)
void Hello(std::string_view XXX)
{
std::cout << "你好," << XXX << "!" << std::endl;
}
// 负责遇见(输入)
std::string Input()
{
std::cout << "遇见谁了?请输入他的名字:";
std::string name;
std::getline(std::cin, name);
return name;
}
int main()
{
std::string 谁;
谁 = Input(); // 遇见谁
Hello(谁); // 问候谁
谁 = Input(); // 纵不遇见你
Hello(谁); // 也想问候你
}