语句与异常处理

学习目标:

语句


学习内容:

一、简单语句

  • 表达式语句: 以;结束的,一个;表示当前语句结束了。表达式语句的作用是执行表达式并丢弃掉求值结果。

    c = a + b;  // 计算a+b的值,并把值赋值给c,做后丢弃计算值
    a + b;      // 这是一句没用意义的表达式语句
    cout << a;  
    
  • 空语句:空语句只有一个单独的分号; 别漏写分号,也别多写分号;

    while (cin >> s && s != "end");  // while语句只包含一句空语句
    c = a + b;;                      // 这里其实是两个语句  包含一个表达式语句和一个空语句
    
    while(iter != v.end());   // 多了一个分号,while循环就结束了 下面的语句就无效了
        ++iter;           
    
  • 复合语句:用{}括起来的语句和声明的序列,称为复合语句,也称为块,一个块就是一个作用域。在块中定义的名字只在这个块内是有效的。空块作用等价于空语句。

    while(val <= 10){  // {}内是一个块 也是一个复合语句
        sum += val;
        ++val;
    }
    

二、语句作用域

  • 定义在控制结构(if、switch、while、for)内部的变量,只能在内部使用,一旦超过控制结构部分,就不能使用了;

    while(int i = get_num())
        cout << i << endl;
    i = 0; // 报错  超过范围了
    
  • 如果需要在控制结构外部使用变量,那么这个变量就必须定义在控制语句之前;

    // 查找vector中第一个负值
    auto beg = v.begin();  
    while(beg != v.end() && *beg > 0)
        ++beg;
    if(beg != v.end())
        // 此时找到第一个负值
    

三、条件语句
1.if语句

  • 两种形式

    if(condition)  // condition为真 执行statement
        statement
    
    if(condition)  // condition为真 执行statement1
        statement1
    else           // condition为假 执行statement2
        statement2  
    
  • 嵌套if语句

    const vector<string> scores = {"F", "D", "C", "B", "A", "A++"};
        int grade = 99;
        string lettergrade;
        if(grade < 60){
            lettergrade = scores[0];
        }else{
            lettergrade = scores[(grade - 50) / 10];
            if(grade != 100){
                if(grade % 10 > 7)  // 8、9 给个+
                    lettergrade += '+';
                else if(grade % 10 < 3)  // 0 1 2 给个-
                    lettergrade += '-';
            }
        }
        cout << lettergrade << endl;
    
  • if-else应该都用花括号{}隔开,增加代码的可读性,也减少错误的可能问题

    if(grade != 100){
        // 本意:89 N+    012 N-   3~7 N
        // 现在:89 N+    012 N    3~7 N-
        if(grade % 10 > 3)  // 1
            if(grade % 10 > 7)  // 2
                lettergrade += '+';
        // 这个else虽然是和1位置对齐,但是它是属于它最近的if 也就是2的
        else
            lettergrade += '-';
    }
    

    花括号修改:

    if(grade != 100){
        if(grade % 10 > 3){  // 1
            if(grade % 10 > 7)  // 2
                lettergrade += '+';
        }
        else{  // 花括号括起来,那么这个else就和1处的if对齐了
            lettergrade += '-';
        }
    }
    

2. switch语句

  • switch可以从若干个固定选项中做出选择。一般形式:

    switch(expression){
        case A: 
            xxx;
            break;
        case B:
            xxx;
            break;
        case C:
            xxx;
            break;
        default:
            xxx;
            break;
    }
    

    执行顺序:首先对switch中的表达式expression求值,再将求值结果转换为整数类型,然后将整数类型和每个case标签的值作对比,如果哪个匹配成功,就从该标签下的第一条语句开始执行,直到碰到break语句或者switch结尾为止。如果匹配不成功,就执行default下面的语句。

    char ch;
    int acnt = 0, ecnt = 0, icnt = 0, ocnt = 0, ucnt = 0;
    cin >> ch;
    // 输入'a'   acnt=1 ecnt=1 icnt=1 ocnt=1 ucnt=1
    switch(ch){
        case 'a': 
            ++acnt;
        case 'e':
            ++ecnt;
            // 如果这里加一个break;  输入'a'  acnt=1 ecnt=1 icnt=0 ocnt=0 ucnt=0
        case 'i':
            ++icnt;
        case 'o':
            ++ocnt;
        case 'u':
            ++ucnt;
    }
    
  • case标签后面对应的值一定是常量表达式;

    char ch = getVal();
    int ival = 42;
    switch(ch){
        case 3.14:  // 错误 case后的值必须是整数
        case ival:  // 错误 case后的值必须是常量
    }
    
  • 最好在switch内写好该写的break和default;

  • 多个case如果执行的相同的功能,其实是可以合并写的;

    // 统计元音和非元音的个数
    switch(ch){
        // 相同的功能,可以叠在一起写
        case 'a': case 'e': case 'i': case 'o': case 'u':
            ++vowelCnt;
            break;
        default:
            ++otherCnt;
            break;
    }
    
  • 如果在case中定义并初始化一个变量,最好是用{},确定其作用域,防止发生错误;

    bool flag = true;
        switch(flag){
            case true:
                string file_name;  // 错误  含义隐式初始化 为""
                int ival = 0;      // 错误  显式初始化
                int jval;          // 正确  只定义 没有初始化
                break;
            case false:
                jval = 2;          // 赋值 正确
        }
    

    修改,增加{}:

    string file_name;
    bool flag = true;
    switch(flag){
        case true:
            {
                string file_name;  // 正确  但是作用域只在当前case
                int ival = 0;      // 正确  但是作用域只在当前case
                int jval;
                break;
            }
            
        case false:
            jval = 2;  // 错误 超出作用域
            if(file_name.empty()):   // 错误 超出作用域
                cout << "empty" << endl;
            break;
    }
    

四、 迭代语句
while和for是先检查条件,再执行循环体;do while是先执行一次循环体,再检查条件。
1. while语句

  • 只要条件condition为真 就一直执行循环体statement;condition可以是表达式也可以是带初始化的变量声明,但是不能为空。

    while(condition)
        statement
    
  • 当不清楚要迭代多少次时,使用while循环比较合适,比如cin>>a;

    vector<int> v;
    int i;
    while(cin >> i){
        v.push_back(i);
    }
    

2. 传统for语句

  • 一般情况:init_statement负责初始化一个值,它只在for循环中执行一次;condition是循环控制条件,为真就执行一次statement,为假for循环就结束;expression一般用来修改init_statement初始的值;

    for(init_statement; condition; expression)
        statement;
    
  • init_statement可以是声明语句、表达式语句或者空语句;init_statement可以定义多个对象,但是只能有一条声明语句,所以多个对象必须是相同类型的;

    vector<int> v = {1, 2, 3};
    // i 和 sz的类型必须一致
    for(decltype(v.size()) i = 0, sz = v.size(); i != sz; ++i)
        v.push_back(v[i]);
    
  • for语句中定义的对象的作用域只能在for循环内,超出for循环不能使用;

  • for语句可以省略init_statement、condition和expression中的任何一个,甚至是全部;
    i. 省略init_statement

    auto beg = v.beg();
    for(; beg != v.end() && *beg >= 0; ++beg)
        ;
    

    ii. 省略condition,相当于在条件部分写了个true,在循环体中必须要有语句负责退出循环,否则将一直循环下去:

    for(int i = 0; ; ++i)
        // 循环体中必须要有终止迭代的程序 如break等
    

    iii. 省略expression,但是这样就要求条件部分或者循环体内必须可以改变迭代变量的值;

    vector<int> v;
    for(int i = 0; i != 10; ){
        v.push_back(i);
        ++i;
    }
    

3. 范围for语句

  • 执行流程:expression必须是一个序列,declaration声明一个变量,序列中每个元素都可以转换为这个变量的类型,每次都会从新初始化这个变量,即每次从序列中拿到下一个元素,之后执行statement语句,直到序列中的元素都取完为止;

        for(declaration : expression)
            statement
    
  • 为了确保变量类型和序列中元素类型一致,一般都用auto自动转换;

    vector<int> v = {1, 2, 3};
    for(auto &r : v)
        r *= 2;
    

4. do while语句

  • do while语句和while语句差不多,唯一的区别:do while语句会先执行一次循环体,在判断条件是否成立,所以do while比较适合用在至少要执行一次循环体的情况下;

    do
        statement;
    while(condition);
    
  • do while用的很少,了解下就可以了;

  • 变量定义不要定义在条件中:

    do{
        a...
    }while(int a = ..)  // 变量定义在变量出现之后 错误
    

五、 跳转语句

1. break语句

  • break语句终止离他最近的while、do while、for或switch语句,并从这些语句后的第一条语句开始继续执行;

    string buf;
    while(cin >> buf && !buf.empty()){
        switch(buf[0]){
            case '-':
                for(...){
                    if(...)
                        break;  // 离开for
                }
            break;  // 离开switch
        }
        break;  // 离开while
    }  
    

2. continue语句

  • continue语句终止最近循环中的当前迭代并立即开始下一次迭代(循环还在继续);

    // 读取单词,且只操作那些第一个字母不是_的单词
    string buf;
    while(cin >> buf && !buf.empty()){
        if(buf[0] != '_')
            continue  // 结束当前迭代,继续输入buf 开始下一次迭代
        // 处理语句
    } 
    
  • continue只能用于while、for、do while等迭代语句中;

3. goto语句

  • goto语句可以无条件跳转到同一函数的另一条语句,但是c++尽量不要使用goto语句;

六、try语句和异常处理
1. throw表达式

  • throw表达式可以在程序发生异常时,抛出异常,终止程序,并不会对异常进行任何的处理;

    Sales_item item1, item2;
    cin >> item1 >> item2;
    if(item1.isbn() != item2.isbn())
        throw runtime_error("Data must refer to same ISBN");
    cout << item1 + item2 << endl;
    

2. try表达式

  • try表达式可以用来处理异常。形式:

    try{
    
    }catch(exception-declaration){
        handler-statements
    }catch(exception-declaration){
        handler-statements
    }
    

    执行try中的语句,如果发生异常,catch接收异常,对比异常类型属于哪一个catch,执行相应catch中的异常处理方式;

    Sale_item item1, item2;
    while(cin >> item1, item2)
    {
        try{
            item1 += item2;
        }catch(runtime_error err){  // 异常处理
            // 打印异常信息  判断是否重新输入
            cout << err.what() << "\n Try Again? Entry y or n" << endl;
            char c;
            cin >> c;
            if(!cin || c == 'n')
                break
        }catch(exception-declaration){
            handler-statements
        }
    }
    

3. 标准异常

  • c++标准库中定义了一组类,用于报告标准库函数遇到的问题。
    i. exception头文件定义了最普通的异常类,它只报告异常类的发生,不提供是什么异常;
    ii. stdexcept头文件定义了几种常见的异常类,可以提供是什么具体的异常;
    iii. new头文件定义了bad_alloc异常类型;
    iv. type_info头文件定义了bad_cast异常类型;

  • stdexcept头文件定义的常见异常类
    i. exception:最常见的问题
    ii. runtime_error:运行时才能检测的问题;
    iii. range_error: 运行时错误,超出值域范围;
    iv. overflow_error:运行时错误,计算上溢;
    v. underflow_error:运行时错误,计算下溢;
    vi. logic_error:程序逻辑错误;
    vii. domin_error: 逻辑错误,参数对应的结果值不存在;
    viii. invalid_argument: 逻辑错误,无效参数;
    ix. length_errr: 逻辑错误,超出对象最大长度;
    x. out_of_range: 逻辑错误,超出有效范围的值;

  • 24
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值