C++ Primer记录_第五章

第五章 语句

5.1 简单语句

  • 一个表达式在结尾加上分号就变成了表达式语句
  • 表达式语句的作用是执行表达式并丢弃掉求值结果。
ival + 5;     //一条没什么实际用处的表达式语句
cout << ival; //一条有用的表达式语句
  • 空语句只含有一个单独的分号。
  • 使用空语句时应该加上注释。
;//空语句
  • 别漏写分号,也别多写分号。
ival = vl + v2;;//正确:笫二个分号表示一条多余的空语句

//出现了糟糕的情况:额外的分号,循环体是那条空语句
while (iter!=svec.end());//while循环体是那条空语句
{
    ++iter;              //递增运算不属于循环的一部分
}
  • 复合语句是指用花括号括起来的语句和声明的序列,复合语句也被称作
  • 一个就是一个作用域。
while (val <= 10)
{
    sum += val; //把sum+val的值赋给sum
    ++val;      //给val加1
}
  • 空块就是指没有任何语句的一对花括号。
while (cin >> s && s != sought)
{ } //空块

5.2 语句作用域

  • 可以在if、switch、while和for语句的控制结构内定义变量。定义在控制结构当中的变量只在相应语句的内部可见,一旦语句结束,变量也就超出其作用范围了。
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

5.3 条件语句

  • C++语言提供了两种按条件执行的语句:if语句switch语句

5.3.1 if语句

  • if语句的作用是判断一个指定的条件是否为真,根据判断结果决定是否执行另外一条语句。
if (/* condition */)
{
    /* statement */
}
  • if else语句
if (/* condition */)
{
    /* statement */
}
else
{
    /* statement */
}
  • 嵌套if语句
if (/* condition */)
{
    /* statement */
}
else if (/* condition */)
{
    /* statement */
}
  • 悬垂else规定else与离它最近的尚未匹配的if匹配,从而消除了程序的二义性。
if (/* condition */)
{
    if (/* condition */)
    {
        /* statement */
    }
}
else if (/* condition */)
{
    if (/* condition */)
    {
        /* statement */
    }
}

5.3.2 switch语句

  • switch提供了一条便利的途径使得我们能够在若干固定选项中做出选择。
#include <iostream>
using namespace std;
//该程序用于说明:函数内部不宜定义与全局变量同名的新变量
int reused = 42; // reused拥有全局作用域
int main()
{
    //为每个元音字母初始化其计数值
    unsigned aCnt = 0, eCnt = 0, iCnt = 0, oCnt = 0, uCnt = 0;
    char ch;
    while (cin >> ch)
    {
        switch (ch)
        {
            case 'a':++aCnt;break;
            case 'e':++eCnt;break;
            case 'i':++iCnt;break;
            case 'o':++oCnt;break;
            case 'u':++uCnt;break;
        }
        if (ch=='/')
        {
            break;
        }
    }
    //输出结果
    cout << "Number of vowel a:" << aCnt << endl;
    cout << "Number of vowel e:" << eCnt << endl;
    cout << "Number of vowel i:" << iCnt << endl;
    cout << "Number of vowel o:" << oCnt << endl;
    cout << "Number of vowel u:" << uCnt << endl;
}
a
e
e
i
i
i
o
/
Number of vowel a:1
Number of vowel e:2
Number of vowel i:3
Number of vowel o:1
Number of vowel u:0
  • case关键字和它对应的值被称为case标签,case标签必须是整型常量表达式。
  • 如果某个case标签匹配成功,将从该标签开始往后顺序执行所有case分支,除非程序显式地中断了这一过程,否者直到switch结尾才会停下来。
  • 大多数在下一个case标签之前应该有一条break语句来防止case穿透。
  • 漏写break容易引发BUG
#include <iostream>
using namespace std;
//该程序用于说明:函数内部不宜定义与全局变量同名的新变量
int reused = 42; // reused拥有全局作用域
int main()
{
    //为每个元音字母初始化其计数值
    unsigned aCnt = 0, eCnt = 0, iCnt = 0, oCnt = 0, uCnt = 0;
    char ch;
    while (cin >> ch)
    {
        switch (ch)
        {
            case 'a':++aCnt;
            case 'e':++eCnt;
            case 'i':++iCnt;
            case 'o':++oCnt;
            case 'u':++uCnt;
        }
        if (ch=='/')
        {
            break;
        }
    }
    //输出结果
    cout << "Number of vowel a:" << aCnt << endl;
    cout << "Number of vowel e:" << eCnt << endl;
    cout << "Number of vowel i:" << iCnt << endl;
    cout << "Number of vowel o:" << oCnt << endl;
    cout << "Number of vowel u:" << uCnt << endl;
}
a
e
e
i
i
i
o
/ 
Number of vowel a:1
Number of vowel e:3
Number of vowel i:6
Number of vowel o:7
Number of vowel u:7
  • 如果没有任何一个case标签能匹配上switch表达式的值,程序将执行紧跟在default标签后面的语句。
#include <iostream>
using namespace std;
//该程序用于说明:函数内部不宜定义与全局变量同名的新变量
int reused = 42; // reused拥有全局作用域
int main()
{
    //为每个元音字母初始化其计数值
    unsigned aCnt = 0, eCnt = 0, iCnt = 0, oCnt = 0, uCnt = 0;
    char ch;
    while (cin >> ch)
    {
        switch (ch)
        {
            case 'a':++aCnt;break;
            case 'e':++eCnt;break;
            case 'i':++iCnt;break;
            case 'o':++oCnt;break;
            case 'u':++uCnt;break;
            default:cout<<"GG"<<endl;return 0;
        }
    }
}
/
GG
  • 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 在作用域内, 但是没有被初始化
    {
        /* statement */
    }
  • 如果需要为某个case分支定义并初始化一个变量,我们应该把变量定义在块内。
case true:
    {
        //正确:声明语句位于语句块内部
        string file_name = get_file_name();
    }
    break;
case false:
    if (file_name.empty()) //错误:file_name不在作用域之内

5.4 迭代语句

  • 迭代语句通常称为循环,它重复执行操作直到满足某个条件才停下来。

5.4.1 while语句

  • 只要条件为真,while语句就重复地执行循环体。
while (/* condition */)
{
    /* statement */
}
  • 当不确定到底要迭代多少次时,使用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语句

  • init-statement必须是以下三种形式中的一种:声明语句、表达式语句或者空语句。
  • condition作为循环控制的条件。
  • 只要condition为真,就执行一次stalement
  • 如果condition第一次的求值结果就是false, 则statement一次也不会执行。
  • expression负责修改init-statement初始化的变量。
for (/*init-statemen*/; /*condition*/; /*expression*/)
{
    /* code */
}
  • 传统for循环的执行流程。
//重复处理s中的字符直至我们处理完全部字符或者遇到了一个表示空白的字符
for (decltype(s.size()) index = 0; index != s.size() && !isspace(s[index]); ++index)
{
    s[index] = toupper(s[index]); //将当前字符改成大写形式
}
  • for语句头中的多重定义。
//记录下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 >= O; ++beg)
{
    ; //什么也不做
}
for (int i = O; /*条件为空*/; ++i)
{
    //对i进行处理,循环内部的代码必须负责终止迭代过程!
}
vector<int> v;
for (int i; cin >> i; /*表达式为空*/) //只要读取完所有的输入或者遇到一个输入错误就终止循环
{
    v.push_back(i);
}

5.4.3 范围for语句

  • C++ll新标准引入了一种种更简单的for语句。
  • expression表示的必须是个序列,如用花括号括起来的初始值列表、数组、或者vector或string等类型的对象。
  • 这些序列的共同特点是拥有能返回迭代器的begin和end成员。
  • declaration定义个变星,序列中的每个元素都得能转换成该变量的类型。确保类型相容最简单的办法是使用auto类型说明符。
for (/*declaration*/ : /*expression*/)
{
    /*statement*/
}
vector<int> v = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
//范围变量必须是引用类型,这样才能对元素执行写操作
for (auto &r : v) //对于v中的每一个元素
{
    r *= 2; //将v中每个元素的值翻倍
}
//范围for语句与之等价的传统for语句
for (auto beg = v.begin(), end = v.end(); beg != end; ++beg)
{
    auto &r = *beg; // r必须是引用类型,这样才能对元素执行写操作
    r *= 2;         //将v中每个元素的值翻倍
}

5.4.4 do while语句

  • do while语句和while语句非常相似,唯一的区别是,do while语句先执行循环体后检查条件。
do
{
    /* statement */
} while (/* condition */);
  • 条件部分检查用户做出的回答,如果用户没有回答,或者用户的回答以字母n开始,循环都将终止。否则循环继续执行。
//不断提示用户输入一对数,然后求其和
string rsp; //作为循环的条件,不能定义在do的内部
do
{
    cout << "please enter two values: ";
    int val1 = 0, val2 = 0;
    cin >> val1 >> val2;
    cout << "The sum of " << val1 << "and" << val2
         << ";" << vall + val2 << "\n\n"
         << "More? Enter yes or no: ";
    cin >> rsp;
} while (!rsp.empty() && rsp(O] != 'n');
  • 因为对于do while来说先执行语句或者块,后判断条件,所以不允许在条件部分。
do
{
    //....
    mumble(foo);
} while (int foo = get_foo());//错误:将变量声明放在了do的条件部分

5.5 跳转语句

  • C++语言提供了4种跳转语句:break、continue、goto、return

5.5.1 break语句

  • break语句负责终止离它最近的while、do while、for、switch语句。
string buf;
while (cin >> buf && !buf.empty())
{
    switch (buf[0])
    {
    case '-':
    {
        //处理到第一个空白为止
        for (auto it = buf.begin + 1; it != buf.end(); ++it)
        {
            if (*it == ' ')
            {
                break; //#1,离开for循环
            }
        }
    }
    //#1将控制权转移到这里
    //剩下的`-`处理
    break; //#2,离开switch语句
    case '+':
        //.......
    } //结束switch
    //#2将控制权转移到这里
}

5.5.2 continue语句

  • continue语句终止最近的循环中的当前迭代并立即开始下一次迭代。
string buf;
while (cin >> buf && !buf.empty())
{
    if (buf[O] != '_')
    {
        continue; //接着读取下一个输入
    }
    //程序执行过程到了这里?说明当前的输入是以下画线开始的;接着处理buf··…·
}

5.5.3 goto语句

  • goto语句的作用是从goto语句无条件跳转到同一函数内的另一条语句。
  • 每天一个离职小技巧,不要在程序中使用goto语句,因为它使得程序既难理解又难修改。
//...
goto end;
int ix = 10; //错误:goto语句绕过了一个带初始化的变量定义。
end :
    //错误:此处的代码需要使用ix,但是goto语句绕过了它的声明
    ix = 42;
  • 向后跳过一个已经执行的定义是合法的。跳回到变量定义之前意味着系统将销毁该变量,然后重新创建它。
//向后跳过一个带初始化的变量定义是合法的
begin:
    int sz=get_size();
    if (sz<=0)
    {
        goto begin;
    }

5.6 TRY语句块和异常处理

  • 异常是指存在于运行时的反常行为,这些行为超出了函数正常功能的范围。
  • 处理反常行为可能是设计所有系统最难的一部分。
  • throw表达式异常检测部分使用throw表达式来表示它遇到了无法处理的问题。我们说throw引发了异常。
  • try语句块异常处理部分使用try语句块处理异常。try语句块以关键字try开始,并以一个或多个catch子句结束。
  • try语句块中代码抛出的异常通常会被某个catch子句处理。因为catch子句“处理”异常,所以它们也被称作异常处理代码

5.6.1 throw表达式

  • 程序的异常检测部分使用throw表达式引发一个异常。
Sales_item item1, item2;
cin >> item1 >> item2;
//首先检查iteml和item2是否表示同一种书籍
if (iteml.isbn() = item2.isbn())
{
    cout << item1 + item2 << endl;
    return0; //表示成功
}
else
{
    cerr << "Data must refer to same ISBN" << endl;
    return -1; //表示失败
}
Sales_item item1, item2;
cin >> item1 >> item2;
//首先检查iteml和item2是否表示同一种书籍
if (iteml.isbn() = item2.isbn())
{
    throw runtime_error("Data must refer to same ISBN");
}
//如果程序执行到了这里,表示ISBN是相同

5.6.2 try语句块

  • try语句块的通用语法形式。
  • catch子句包括三部分:关键字catch、括号内一个对象的声明(称作异常声明)、以及一个块。
  • program-statements组成程序的正常逻辑,像其他任何块一样,可以有包括声明在内的任意C++语句。
try
{
    /* program-statements */
}
catch (exception - declaration)
{
    /* handler-statements */
}
catch (exception - declaration)
{
    /* handler-statements */
} //.....
  • 编写处理代码。
while (cin >> item1 >> 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循环
        }
    }
}

5.6.3 标准异常

  • C++标准阵定义了一组类,用于报告标准库函数遇到的问题。
  • 别定义在4个头文件中:
    1. exception头文件定义了最通用的异常类exception。它只报告异常的发生,不提供任何额外信息。
    2. stdexcept头文件定义了几种常用的异常类。
    3. new头文件定义了bad_alloc异常类型。
    4. type_info头文件定义了bad_cast异常类型。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Flame老唐

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值