加载中...
Hello World 分支版
第1节:初学C++,应该学什么?
第2节:《白话C++》练的什么“功”?
第3节:《白话C++》练的什么“武”?
第4节:打开浏览器,线上玩转C++
第5节:逐字逐句,深入理解C++最小例程
第6节:做一个“会”犯错误的程序员
第7节:Hello World 中文版
第8节:Hello World 函数版
第9节:Hello World 交互版
第10节:Hello World 分支版
第11节:Hello World 循环版
第12节:Hello Object 生死版.上
第13节:Hello Object 生死版. 下
第14节:Hello Object 成员版
第15节:Hello Object 派生版
第16节:Hello Object 多态版
课文封面
  1. 通过多级分支流程,实现上班路上遇见女神、老板娘、普通同事的差别化问候;
  2. 比较操作、特别是如何正确比较字符串是否相等;
  3. 逻辑操作:或者(以及 并且);
  4. 学习《歧路亡羊》。

0. 课堂视频

1. 分支流程

最常见的程序执行流程,是一条路顺序走到底,称为“顺序流程”,比如:

语句1; 语句2; 语句3; 语句4;

执行过程(为节约版面,横着摆)就是:

顺序流程

但人生最不缺的,就是选择。有时候,我们希望在执行完语句1之后,如果具备某个条件,才执行语句2,否则执行语句3;最后再执行语句4,这时的流程就是一个典型的选择分支流程。

  • 流程图

分支流程示例

注意,上图中,语句1在分支之前,语句4在分支之后,因此都是必经之路,必做的步骤;二者并不属于分支流程的组成。

套用课程视频里老师所说的 “差别式问候”,则有:

  1. 条件1:指当前遇到的XXX,是不是 “志玲”;
  2. 语句2:当条件1成立,执行的语句,即对女神的问候;
  3. 语句3:当条件1不成立,执行的语句,即普通问候。

下面给出典型的 if/else 代码结构和实际使用案例代码——

  • 代码结构:
if (条件1) { // 条件1成立时,走这里的代码 } else { // 否则(即:条件1不成立),走这里的代码 }
  • 案例代码:
void Hello(std::string_view XXX) { if (XXX == "志玲") { std::cout << "你好,志玲,你很美,你是站长的女神" << std::endl; } else { std::cout << "你好," << XXX << "!" << std::endl; } }

如果需要区分处理的情况,有更多个,那就需要多级 if / else if / else if / …,我们先给三级的情况:

三级 if/else if/ else

  • 代码结构:
if (条件1) { // 条件1成立时,走这里的代码 } else if (条件2) { // 条件1不成立,但条件2成立,走这里的代码 } else { // 条件1和2都不成立,走这里的代码 }
  • 案例代码:
void Hello(std::string_view XXX) { if (XXX == "志玲") { std::cout << "你好,志玲,你很美,你是站长的女神" << std::endl; } else if (XXX == "老板娘") { std::cout << "你好,财神!" << std::endl; } else { std::cout << "你好," << XXX << "!" << std::endl; } }

2. 比较运算

在分支结构的条件判定中,经常需要用到比较运算。常见的比较运算符如:

  1. == (是否相等,注:可能是内容相等比较,也可能是实体相等比较)
  2. >、>=、<、<= (是否大于、大于等于、小于、小于等于,同样有可能是在比较内容大小,也可能是在比较实体,即数据所在地址/“门牌号码”的大小)

2.1 实体 vs. 内容

  • 程序中数据的实体,是指这个数据所拥有的独立身份,即它所存在的内存地址。
  • 程序中数据的内容,是指这个数据所拥有的内容,即它所存在的内存所存储的值。

比如,下图所示的内存中,有两个字符串,二者实体地址不同(一个701,一个705),但内容相同:

实体与内存

2.2 正确比较字符串

依据数据类型不同,比较操作有可能是在比较数据的内容,也可能是在比较数据的实体(所在内存地址)。

纯C风格的字符串(即字符指针)比较,比较的是实体,所以:

if ("志玲" == "志玲") { cout << "志玲就是读者老婆" << endl; // 这句话基本不可能输出 }

或者:

char const* a = "志玲"; // 定义一个常量字符串指针,变量定义见上节
char const* b = "志玲"; 

if (a == b)
{
   cout << "志玲就是读者老婆" << endl; // 这句话基本不可能输出
}

不能说志玲就绝对不可能是读者的老婆,因为在特定编译条件设定下,以及编译优化下,上面条件有可能成立,此时两个内容相同的字符串,会被编译器刻意实现成就是同一个实体;但是!我们写的程序,一定不能依赖于这个比较结果,因为在复杂的程序中,这个相等结果无法保障。

编译器之所以会在此类情况下,做优化,主要是为了节省内存:让所有内容相同的字符串,就变成是同一个字符串。程序逻辑不能依赖于优化的“副”作用。

下面代码即是一个内容相等,但实体确保不等(给编译器十个胆子,也不敢优化):

#include <iostream> #include <cstring> using namespace std; int main() { char* a = new char[4]; // 给a单独分配一块内存 std::strcpy(a, "AAA"); // 并使用 "AAA" 填充其内容 if (a == "AAA") // 肯定不等 { cout << "a is AAA" << endl; } }

业务程序中的多数情况,当我们比较字符串时,希望比较的是内容,C++ 标准库的 std::string以及来自17新标的 std::string_view ,体现了这一点。因此,假设有:

std::string a1 = "AAA"; std::string a2 = "AAA"; std::string_view av1 = a1; std::string_view av2 = a2;

则以下相等比较都成立:

  • a1 == a2
  • a1 == av1
  • a2 == av1
  • a2 == av2
  • av1 == av2

为了进一步方便 “比较字符串内容”,而非“比较字符串实体”,C++还实现了一点小小的“智能”:只要等号(==)两边有一个是 std::string 或 std::string_view,则另一边的数据,在存在可能的情况下——即程序中存在允许将该数据隐式的一步转换成std::string 或std::string_view 时——编译器就会做这个转换,从而实现 == 两边都是 std::string 或 std::string_view后,再作比较。

3. 逻辑运算 || 和 &&

在分支结构的条件判定中,经常需要用到逻辑(组合)运算。

  • && 是“并且/AND”的意思,左右两个条件都为真,结果才是真。
  • || 是“或者/OR”的意思,左右两个条件有一个为真,结果就是真。

所以,如果我们认定志玲和娜扎都是女神,那么,以下条件就写得很棒:

if (XXX == "志玲" || XXX == "娜扎") { cout << "你好," << XXX << ",女神!" << endl; }