欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

《C++ Primer中文版》第五章:语句

程序员文章站 2024-03-11 11:41:37
...

 

 

第五章:语句

5.1简单语句

C+ +语言提供了一组控制流( flow-of-control)语句以支持更复杂的执行路径。

表达式语句( expression statement)

ival + 5;//一条没什么实际用处的表达式语句
cout << ival;// 一条有用的表达式语句 

空语句( null statement )

; //空语句
//重复读入数据直至到达文件末尾或某次输入的值等于sought
while (cin >> s && s!= sought)
; //空语句

《C++ Primer中文版》第五章:语句

别漏写分号也别多写分号

//出现了糟糕的情况:额外的分号,循环体是那条空语句
while(iter != svec.end()) ;// while循环体是那条空语句
++iter ;//递增运算不属于循环的一部分

《C++ Primer中文版》第五章:语句

复合语句(块) compound statement

while (val <= 10) {
    sum += val;//把sum + val的值赋给sum。
    ++val ;//给val加1
}


《C++ Primer中文版》第五章:语句

所谓空块,是指内部没有任何语句的一对花括号。 空块的作用等价于空语句:
while (cin >> s && s!=sought)
  { } //空块

5.2语句作用域

while (int i = get_num()) //每次迭代时创建并初始化i
    cout<<i<<endl;
i =0;//错误:在循环外部无法访问i
如果其他代码也需要访问控制变量,则变量必须定义在语句的外部:
// 寻找第一个 负值元素
auto beg = v.begin() ;
while (beg != v.end() && beg >= 0)
    ++beg;
if (beg == v.end() )
    //此时我们知道v中的所有元素都大于等于0
因为控制结构定义的对象的值马上要由结构本身使用,所以这些变量必须初始化。
//错误的:
1. while (string::iterator iter != s.end()) { /* ... */ }
2. while (bool status = find (word)) { /*...*/ }
    if (!status) { /* ... */ }

//正确的:
string::iterator iter=s.begin() ;
while (iter != s.end() )
{
    ++iter;
    /*...*/
}

//正确的:
bool status;
while (status = find (word)) { /*.. .*/ }
if (!status) { /* */ }

5.3.1 if 语句

嵌套if语句
使用ifelse语句
注意使用花括号


悬垂else

当一个if语句嵌套在另一个if语句内部时,很可能if分支会多于else分支。事
实上,之前那个成绩转换的程序就有4个if分支,而只有2个else分支。这时候问题
出现了:我们怎么知道某个给定的else是和哪个if匹配呢?   
悬垂else ( dangling else )

//错误:实际的执行过程并非像缩进格式显示的那样; else分支匹配的是内层if语句
if (grade % 10 >= 3)
    if (grade % 10 > 7)
           lettergrade += '+'; //末尾是8或者9的成绩添加一个加号
else
     lettergrade += '-'; //末尾是3、4、5、6或者7的成绩添加一个减号!
//缩进格式与执行过程相符,但不是程序员的意图
if (grade%10 >= 3)
    if (grade%10 > 7)
        lettergrade += '+'; 11末尾是8或者9的成绩添加一个加号
    else
        lettergrade += '-'; 1/末尾是3、4、5、6或者7的成绩添加一个减号!

使用花括号控制执行路径

//末尾是8或者9的成绩添加一个加号,末尾是0、1或者2的成绩添加一个减号
if (grade % 10 >=3){
    if(grade % 10>7)
        lettergrade += '+'; //末尾是8或者9的成绩添加一个加号
}else//花括号强迫else与外层if匹配
    lettergrade += '-' ;//末尾是0、1或者2的成绩添加一个减号

5.3.2 switch 语句

case关键字和它对应的值一起被称为case标签(case label)。case 标签必须是整型常量表达式:
 

char ch = getVal () ;
int ival =42;
switch (ch){
case 3.14: //错误: case标签不是一个整数
case ival: //错误: case标签不是一个常量
//
...

任何两个case标签的值不能相同,否则就会引发错误。另外,default也是一种特殊的case标签,关于它的知识将在第162 页介绍。
switch内部的控制流

写在一行里,强调这些case代表的是某个范围内的值:
switch (ch)
{
   //另一种合法的书写形式
   case 'a': case 'e': case 'i': case 'O': case 'u':
      ++ vowelCnt;
      break;
}

《C++ Primer中文版》第五章:语句

漏写break容易引发缺陷
《C++ Primer中文版》第五章:语句

default标签(default label )
《C++ Primer中文版》第五章:语句

switch内部的变量定义

如果在某处一个带有初值的变量位于作用域之外,在另一处该变量位于作用域之内,则从前一处跳转到后-处的行为是非法行为。

case true://因为程序的执行流程可能绕开下面的初始化语句,所以该switch语句不合法
    string file_name;//错误:控制流绕过一个隐式初始化的变量
    int ival = 0;//错误:控制流绕过一个显式初始化的变量
    int jval;//正确:因为jval没有初始化
    break; 
case false ://正确: jval虽然在作用域内,但是它没有被初始化
    jval = next_num() ;//正确:给jval赋一个值
    if (file_name.empty() )// file name在作用域内,但是没有被初始化
// ...

C++语言规定,不允许跨过变量的初始化语句直接跳转到该变量作用域内的另一个位置。

5.4迭代语句

5.4.1 while 语句
《C++ Primer中文版》第五章:语句

使用while循环

vector<int> V;
int i;  //重复读入数据,直至到达文件末尾或者遇到其他输入问题
while (cin >> i)
     v.push back(i) ; //寻找第一个负值元素
auto beg = v.begin() ;
while (beg != v.end() && *beg >= 0)
    ++beg ;
if (beg == v.end() )
    //此时我们知道V中的所有元素都大于等于0        

5.4.2传统的 for语句

//重复处理s中的字符直至我们处理完全部字符或者遇到了一个表示空白的字符
for (decltype(s.size()) index = 0;
    index != s.size() && !isspace(s[index]) ; ++index)
        s[index] = toupper(s [index]); //将当前字符改成大写形式

for语句头中的多重定义
init-statement 也可以定义多个对象。但是init-statement 只能有一条声明语句,因此,所有变量的基础类型必须相同。

//记录下v的大小,当到达原来的最后一个元素后结束循环
for (decltype(v.size()) i = 0, sz = v.size() ; i!= sz; ++i)
    v.push back(v[i]) ;

省略for语句头的某些部分

auto beg = v.begin() ;
    for ( /*空语句*/; beg != v.end() && *beg >= 0; ++beg) 
               ; //什么也不做

因为条件部分能改变i的值,所以这个循环无须表达式部分。其中,条件部分不断检查输入流的内容,只要读取完所有的输入或者遇到一个输入错误就终止循环。

vector<int> V;
for(int i;cin >> i;/*表达式为空*/)
    v.push_back(i) ;

 

5.4.3 范围for语句 c++11

看之前的代码理解。

学习了范围for语句的原理之后,我们也就不难理解为什么强调不能通过范围for语句增加vector对象(或者其他容器)的元素了。在范围for语句中,预存了end()的值。一旦在序列中添加(删除)元素,end函数的值就可能变得无效了。

5.4.4 do while语句
《C++ Primer中文版》第五章:语句

5.5跳转语句

跳转语句中断当前的执行过程。C++语言提供了4种跳转语句: break、 continue、goto和return

5.5.1 break 语句
5.5.2continue 语句

5.5.3goto语句

 

5.6try语句块和异常处理

异常是指存在于运行时的反常行为,这些行为超出了函数正常功能的范围。典型的异常包括失去数据库连接以及遇到意外输入等。处理反常行为可能是设计所有系统最难的一部分。
当程序的某部分检测到一个它无法处理的问题时,需要用到异常处理。此时,检测出问题的部分应该发出某种信号以表明程序遇到了故障,无法继续下去了,而且信号的发出方无须知道故障将在何处得到解决。一旦发出异常信号,检测出问题的部分也就完成了任务。
如果程序中含有可能引发异常的代码,那么通常也会有专门的代码处理问题。例如,如果程序的问题是输入无效,则异常处理部分可能会要求用户重新输入正确的数据;如果丢失了数据库连接,会发出报警信息。异常处理机制为程序中异常检测和异常处理这两部分的协作提供支持。在C++语言中,异常处理包括:
throw表达式( throw expression), 异常检测部分使用throw表达式来表示它遇到了无法处理的问题。我们说throw引发(raise) 了 异常。
try语句块(try block),异常处理部分使用try语句块处理异常。try 语句块以关键字try开始,并以一个或多个catch子句(catch clause)结束。try 语句块中代码抛出的异常通常会被某个catch子句处理。因为catch子句“处理”异常,所以它们也被称作异常处理代码(exception handler)。
●一套异常类(exception class),用于在throw表达式和相关的catch子句之间传递异常的具体信息。
 

 

5.6.1throw表达式

程序的异常检测部分使用throw表达式引发-一个异常。throw表达式包含关键字throw和紧随其后的一个表达式,其中表达式的类型就是抛出的异常类型。throw表达式后面通常紧跟一个分号,从而构成一条表达式语句。

//首先检查两条数据是否是关于同一种书籍的
if (iteml.isbn() != item2.isbn() )
    throw runtime_error ( "Data must refer to same ISBN") ;
//如果程序执行到了这里,表示两个ISBN是相同的
cout << iteml + item2 <<endl ;

5.6.2try语句块

跟在try块之后的是一个或多个catch子句。catch子句包括三部分:关键字catch、括号内一个(可能未命名的)对象的声明(称作异常声明,exception declaration)以及一个块。

当选中了某个catch子句处理异常之后,执行与之对应的块。catch一旦完成,程序跳转到try语句块最后一个catch子句之后的那条语句继续执行。
try语句块中的program-statements组成程序的正常逻辑,像其他任何块样,program-statements可以有包括声明在内的任意C++语句。一如往常,try语句块内声明的变量在块外部无法访问,特别是在catch子句内也无法访问。
 

while (cin >> iteml >> item2) {
    try {
        //执行添加两个Sales_ item 对象的代码
        //如果添加失败,代码抛出一个runtime_ error 异常
    } catch (runtime_error err) {
        //提醒用户两个ISBN必须一致,询问是否重新输入
        cout << err.what ()
                << "\nTry Again? Enter y or n" << endl ;
         char c;
         cin >> c;
         if (!cin || c =='n')
            break; // 跳出while循环
         }
}

函数在寻找处理代码的过程中退出
在复杂系统中,程序在遇到抛出异常的代码前,其执行路径可能已经经过了多个try语句块。例如,一个try语句块可能调用了包含另一个try语句块的函数,新的try语句块可能调用了包含又一个try语句块的新函数,以此类推。

寻找处理代码的过程与函数调用链刚好相反。当异常被抛出时,首先搜索抛出该异常的函数。如果没找到匹配的catch子句,终止该函数,并在调用该函数的函数中继续寻找。如果还是没有找到匹配的catch子句,这个新的函数也被终止,继续搜索调用它的函数。以此类推,沿着程序的执行路径逐层回退,直到找到适当类型的catch子句为止。

如果最终还是没能找到任何匹配的catch子句,程序转到名为terminate的标准库函数。该函数的行为与系统有关,一般情况下, 执行该函数将导致程序非正常退出。

对于那些没有任何try语句块定义的异常,也按照类似的方式处理:毕竟,没有try语句块也就意味着没有匹配的catch子句。如果一段程序没有 try语句块且发生了异常,系统会调用terminate函数并终止当前程序的执行。
《C++ Primer中文版》第五章:语句

《C++ Primer中文版》第五章:语句
5.6.3标准异常

●exception 头文件定义了最通用的异常类exception。 它只报告异常的发生,不提供任何额外信息。
●stdexcept 头文件定义了几种常用的异常类
●new头文件定义了bad alloc异常类型
●type_ info头文件定义了bad cast异常类型



《C++ Primer中文版》第五章:语句

标准库异常类只定义了几种运算,包括创建或拷贝异常类型的对象,以及为异常类型的对象赋值。
我们只能以默认初始化的方式初始化exception、bad_alloc 和bad_cast 对象,不允许为这些对象提供初始值。
其他异常类型的行为则恰好相反:应该使用string对象或者C风格字符串初始化这些类型的对象,但是不允许使用默认初始化的方式。当创建此类对象时,必须提供初始值,该初始值含有错误相关的信息。
异常类型只定义了一个名为what的成员函数,该函数没有任何参数,返回值是一个指向C风格字符串的const char*。 该字符串的目的是提供关于异常的一.些文本信息。

what函数返回的C风格字符串的内容与异常对象的类型有关。如果异常类型有一个字符串初始值,则what返回该字符串。对于其他无初始值的异常类型来说,what返回的内容由编译器

上一篇: Nmap-01:Nmap的介绍与安装

下一篇: