目录

C-primer第五章

《C++ primer》第五章

空语句:空语句只有一个单独的分号。

复合语句(块):一个块就是一个作用域,块不需要以分号结束,快内可包含多条语句。

一、条件语句

1.1、if语句

/*if语句的作用是:判断一个指定的条件是否为真,根据判断结果决定是执行相关块内命令*/

//第一种形式
if (condition)
    statement

//第二种形式
if (condition)
    statement1
else 
    statement2

/*if-else例子*/
const vector<sting> scores = {"F", "D". "C", "B", "A", "A++"};
string lettergrade;
int grade;
cin >> grade;
if (grade < 60)
    lettergrade = scores[0];
else 
    lettergrade = scores[(grade - 50) / 10);

/*嵌套if-else*/
if (grade < 60)
    lettergrade = scores[0];
else {
    lettergrade = scores[(grade - 50) / 10);
    if (grade != 100)
        if (grade % 10 > 7)
            lettergrade += '+';
        else if (grade % 10 < 3)
            lettergrade += '-';
}

1.2、switch语句

/*switch提供了一条便利的途径使得我们能够在若干固定选项中做出选择*/

/*switch语句示例*/
unsiged 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;
    }
}
/*表达式紧跟在switch之后,表达式的值转化成整数类型,然后与case标签的值比较
 *case关键字和它对应的值一起被称为case标签,case标签必须是一个常量表达式
 *任何两个case标签的值不能相同
 *若某个case标签匹配成功,将从该标签开始往后顺序执行所有case分支,除非使用break中断
 *虽然在最后一个case,不用break也可以,但是最好还是加上*/

/*每个case标签只能对应一个值,而多个表格标签可以对应同一个值,例如:统计元音字母出现的总次数*/
unsigned vowelCnt = 0;
char ch;
while (cin >> ch) {
    switch (ch) {
        case 'a':
        case 'e':
        case 'i':
        case 'o':
        case 'u':
            ++vowelCnt;
            break;
    }
}

/*如果没有任何一个case标签能匹配上,程序将执行紧跟在default标签后面的语句(如果有的话)
 *如果default标签后为空,则default后必须跟上一个空语句或者空块*/
unsigned vowelCnt = 0;
unsigned otherCnt = 0;
char ch;
while (cin >> ch) {
    switch (ch) {
        case 'a': case 'e': case 'i': case 'o': case 'u':
            ++vowelCnt;
            break;
        default:
            ++otherCnt;
            break;
    }
}

/*如果需要为某个case分支定义并初始化一个变量,应该把变量定义在块内,
 *从而保证后面的所有case标签都在变量的作用域之外*/
case true:
    {
        string file_name = get_file_name();
        ……
    }
case false:
    if (file_name.empty()) {……}           //错误,file_name不在作用域内

二、迭代语句

迭代语句通常称为循环,重复执行操作直到满足某个条件才停下来。while和for在执行循环体之前检查条件,do while语句先执行循环体再检查条件

2.1、while语句

/*while的基本语法:只要condition一直为true,就会一直执行statement*/
while (condition)
    statement


/*condition可以是一个表达式或者一个带初始化的变量声明*/
int val = 0;
while (val <= 10) {……}

while (int i = get_num())   //每次迭代时创建并初始化i


/*什么时候比较适合使用while:
 *Ⅰ、不确定要迭代多少次,如读取输入
 *Ⅱ、需要在循环结束后访问循环控制变量*/
vector<int> vec;
int i;
while (cin >> i) 
    vec.push_back(i);
auto beg = vec.begin();
while (beg != vec.end() && *beg >= 0)
    ++beg;
if (beg == vec.end())
    cout << "没有负数" << endll;

2.2、传统for循环

/*for循环的基本语法:*/
for (init-statement; condition; expression)
    statement

/*init-statement可以是声明语句、表达式语句和空语句。可以定义多个对象(但是类型必须相同)
 *condition为循环控制条件,如果为真则继续循环,如果为假则退出循环。若此处为空,相当于是true
 *expression负责修改控制变量,当然也可以为空*/
for (decltype(vec.size()) i = 0, sz = vec.size(); i != sz; ++i)
    vec.push_back(v[i]);

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

for (int i = 0; ; ++i) {
    ……
}

vector<int> vec;
for (int i; cin >> i; )
    vec.push_back(i);

2.3、范围for语句

/*范围for可以遍历容器或其它序列的所有元素,其基本语法为:*/
for (declaration : expression)
    statement

/*expression必须是一个序列,例如数组、vector、string等,这些类型拥有返回迭代器begin和end的成员
 *declaration定义一个变量,每次迭代都重新定义循环控制变量并将其初始化为序列中的下一个值
 *可以通过auto确定控制变量的类型,如果需要改变序列中的元素值,循环变量要声明为引用*/
vector<int> vec = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
for (auto &ref : vec)
    ref *= 2;

//范围for语句定义来源于与之等价的传统for循环
for (auto beg = vec.begin(), end = vec.end(); beg != end; ++beg) {
    auto &r = *beg;
    r *= 2;
}

/*不能通过范围for增加vector对象(或者其它容器)的元素*/

2.4、do while语句

/*do while语句的基本语法为:*/
do
    statement
while (condition);

/*condition不能为空
 *先执行再判断
 *不允许在条件部分定义变量*/

string rsp;
do {
    cout << "please enter teo values:";
    int val1 = 0, val2 = 0;
    cin >> val1 >> val2;
    cout << "The sum of " << val1 << " and " << val2 << " = "
    << val1 + val2 << "\n\n" << "More ? Enter yes or no:";
    cin >> rsp;
} while (!rsp.empty() && rsp[0] != 'n');

三、跳转语句

3.1、break语句

/*break语句负责终止离它最近的while、do while、for或switch语句,
 *并从这些语句之后的第一条语句开始执行,只能出现在迭代语句/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;    //离开for循环
            }                 //控制权转移给switch
            break;            //离开switch语句
        case '+':
        ……
    }
}

3.2、continue语句

/*continue语句终止最近的循环中的当前迭代并立即开始下一次迭代
 *continue中断当前迭代,但仍在继续执行循环
 *continue语句只出现在for、while、do while循环的内部
 *作用于离它最近的循环
 *只有当switch语句嵌套在迭代语句内部时,才能在switch里使用continue*/

3.3、goto语句

看来这个goto语句风评是真不好,不学了

四、try语句块和异常处理

  • 异常是指存在于运行时的反常行为,这个行为超出了函数正常的功能范围。比如:失去数据库连接、遇到意外输入
  • 异常处理机制为程序中 异常检测 和 异常处理 这两部分的协作提供支持
/*异常处理包括:
 *Ⅰ、throw表达式,异常检测部分使用throw表达式来表示遇到了无法处理的问题
 *Ⅱ、try语句块,异常处理部分使用try语句块处理异常。try语句块以关键字try开始,
 *并以一个或多个catch子句结束,try语句块抛出的异常通常会被某个catch子句处理
 *Ⅲ、一套异常类,用于在throw表达式和相关的catch子句之间传递异常的具体信息*/

4.1、throw表达式

/*throw表达式包括关键字throw和紧随其后的一个表达式,其中表达式的类型就是抛出的异常类型,
 *throw表达式后面通常紧跟一个分号*/

Sales_item item1, item2;
cin >> item1 >> item2;
if (item1.isbn() == item2.isbn()) {
    cout << item1 + item2 << endl;
    return 0;
}else {
    cerr << "Data must refer to same ISBN" << endl;
    return -1;
}

/*在真实程序中,应该把对象相加的代码和用户交互的代码分离开*/
if (item1.isbn() != item2.isbn())
    throw runtime_error("Data must refer to same ISBN")l
//如果程序执行到此,说明两个ISBN相同
cout << item1 + item2 << endl;

4.2、try语句

/*try语句块基本语法:*/
try {
    program-statements
} catch (exception-declaration) {
    handler-statementss
} catch (exception-declaration) {
    handler-statementss
} //…

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 ch;
        cin >> ch;
        if (!cin || ch == 'n')
            break;
    }
}

/*what是runtime_error类的一个成员函数,返回的是一个初始化一个具体对象是所用的string对象副本
 *如果throw编写代码抛出异常,则catch子句输出
 *Data must refer to same ISBN
 *Try Again? Enter y or n*/

/*当程序抛出异常时,首先搜索抛出该异常的函数,如果没有匹配的catch,终止该函数,
 *并在调用该函数的函数中寻找
 *以此类推
 *如果最终还是匹配不到,则转到名为terminate的标准库函数,使程序非正常退出*/

4.3、标准异常

/*四个头文件:
 *Ⅰ、exception->exception
 *Ⅱ、stdexcept
 *Ⅲ、new->bad_alloc
 *Ⅳ、type_info->bad_cast*/


/*标准库异常类只定义了几种运算
 *包括创建/拷贝异常类型的对象,为异常类型的对象赋值
 *以默认方式初始化exception、bad_alloc、bad_cast对象,不能为其提供初始值
 *其它异常类型需要使用string对象/C风格字符串初始化,不允许默认初始化
 *异常类型只有what成员函数,提供关于异常的一些文本信息*/

提示:

  • 如果需要为某个case分支定义并初始化一个变量,应该把变量定义在块内,从而保证后面的所有case标签都在变量的作用域之外
  • 一般而言,catch中的异常声明会使用const和&对对象进行修饰。使用const,可以防止异常对象被修改,异常对象一般是用来输出错误信息的,不需要修改;使用&,可以避免拷贝,节省开销,并且可以支持多态