C++ primer 学习笔记——第五章

简单语句

空语句

;            //空语句

在程序的某个地方,语法上需要一条语句但逻辑上不需要,此时应该使用空语句。

使用空语句时应该加上注释,从而令度这段代码的人知道这句语句是有省略的。


复合语句(compound statement)

用花括号括起来的语句和声明序列,复合语句也被称为块(block)。将其转化成一条复合语句

while (val <= 40)
{
    sum += val;
    ++val;
}                    //while只能容纳一条语句,要执行的语句括起来后转化成一条(复合)语句

语句作用域

可以if、switch、while和for语句的控制结构内定义变量。定义在控制结构当中的变量只能在相应语句内部可见,一旦语句结束,变量也就超过其作用范围。

while(int i = get_num())
    cout << i << endl;
i = 0;                    //错误

悬垂else(dangling else)

在那些既有if语句又有if else语句的语言中是个普遍的问题。C++的解决方案离他最近尚未匹配的if.

if (grade % 10 >= 3)
    if(grade % 10 > 7)
        lettergrade += '+';
else
    lettergrade += '-';
//代码编辑时
//真实编译时
if (grade % 10 >= 3)
    if(grade % 10 > 7)
        lettergrade += '+';
    else
        lettergrade += '-';

switch语句

注:①如果switch语句表达式和所有case都没有匹配上,将直接跳转到switch结构之后的第一条语句。

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

char ch = getVal();
int ival = 42;
switch(ch)
{
    case 'a':     ;        //正确
    case 3.14:    ;        //错误
    case ival:    ;        //错误
}

         ③如果某个case标签匹配成功,将从标签开始循序执行所有case分支【break语句的原因】

         ④一定不要省略case分支最后的break语句。如果没有break语句,最好加一段注释说明清楚程序的逻辑。

         ⑤default标签:即使不准备在default标签下做任何工作,定义一个default标签也是有用的。其目的在于告诉程序的读者,我们已经考虑到了默认的情况,只是目前什么也没做。

         ⑥在C++中,switch-case中的case实质上只是一个标签(label),就像goto的标签一样。case中的代码并没有构成一个局部作用域,虽然它的缩进给人一种错觉,好像它是一个作用域。也就是说,所有在case里面定义的变量作用域都是switch{...},

switch(selector)
{
    cout << selector;
    case selector_a:
        int i = 1;
    case selector_b:
        // i在此仍然可见
}

 如果一个程序的执行路径从代码中的点A(某个局部变量x还未定义)跳到代码中另一点B(该局部变量x已定义,并且定义的时候有初始化),那么编译器会报错。

         ⑦ 在case中定义变量时必须在周围加{..}以形成局部作用域,否则编译报错。习惯上总是将case中的代码用{..}括起来,比较省事。是有些公司的coding convention之一。

case true:
    {
        string file_name = get_file_name();
    }

迭代语句

传统for循环语句

注:①如果第一次迭代时条件就为假,for循环体一次也不会执行

for (int i = 0; i <= -1; ++i)
    cout << 'a' << endl;            //一个也没有

       ②init_statement只能有一条声明语句。因此,所有变量的基础类型必须相同

for(decltype(v.size()) i = 0, sz = v.size(); i != sz; ++i )
    v.push_back(v[i]);

       ③for语句头能省略掉init-statement、condition和expression中的任意一个(或者全部)

auto beg = v.begin();
for(           ; beg != v.end() && *beg >= 0 ; ++beg)
    ;
auto beg = v.begin();
for( int  i = 0 ;            ; ++i)
    //必须存在负责终值迭代的语句
vector <int> v;
for (int i; cin >> i;         )
    v.push_back(i);

范围for语句(C++11)

for(declaration  : expression)
    statement

expression:   表示的必须是一个序列,用花括号括起来的初试值列表,vector或string等类型的对象

declaration:   表示的是一个变量(auto类型说明符)

for语句的定义来源于与之等价的传统for语句;

【如此可以理解】不能通过范围for语句增加vector对象(或者其他容器)的元素了。

在范围for语句中,预存了end()的值。一旦在序列中添加(删除)元素,end函数的值就可以变得无效了;


跳转语句

C++语言提供了4种跳转语句:break,continue,goto和return。

break语句

负责终止距离它最近while、do while、 for或switch语句【没有if!!!】,并从这些语句之后的第一条语句开始继续执行。

string buf;
	while (cin >> buf && !buf.empty())
	{
		switch (buf[0])
		{
			case '-' :
			{
				for (auto it = buf.begin() + 1; it != buf.end(); ++it)
					if (*it == ' ')
						break;                                            //跳转case结束
			}
			break;                                                        //跳转switch结束
			case '+' :
			{
				;
			}
			default:
				break;
			}
		break;                                                            //跳转while结束
	}

continue语句

终止最近的循环中的当前迭代并立即开始下一次迭代。

只能出现在for、while、do while循环的内部,或者镶嵌在此类循环类的语句或块的内部。

string buf;
while (cin >> buf && !buf.empty())
{
    if(buf[0] != '_')
    {
        continue;    
    }
}

与break比较:相同:仅作用于离它最近的循环;

                       不同:只有当switch语句嵌套在迭代语句内部时,才能在switch里使用continue。


goto语句

作用:从goto语句无条件跳转到同一函数内部的另一条语句【不要在程序中使用,使得程序难以理解并且难以修改】

goto label;

注:①goto语句和控制权转向的那条带标签的语句必须位于同一个函数之内

       ②带标签语句(labeled statement)

end:return;        //带标签语句,可以作为goto目标

       ③标签标识符独立于变量或其他标识符的名字

       ④和switch语句相同,goto语句不能将程序的控制权从变量的作用域之外转移到作用域之内

goto end;

int ix = 10;
//标签绕过了它的声明,错误
end:ix = 42;
begin:
    int sz = get_size();
    if (sz <= 0)
    {
        goto begin;    
    }
//成立 语句将销毁zs,从新定义并初始化

try语句块和异常处理

异常处理机制为程序中异常检测异常处理这两部分的协作提供支持

throw表达式(throw expression):异常检测部分使用throw表达式来表示它遇到了无法处理的问题。我们说throw引发(raise)了异常

try语句块(try block):异常处理部分使用try语句处理异常。

throw表达式

在这段代码中,会抛出一个异常,该异常的类型runtime_error的对象。抛出异常时终止当前函数,并把控制权移交给可以处理异常的代码。

#include <stdexcept>
using std::runtime_error;
#include <new>
using std::bad_alloc;
throw (表达式:其类型就为异常类型);
throw  bad_alloc();
throw runtime_error("error!");;
#include <stdexcept>
using std::runtime_error;
int main()
{
	vector<string> buf{"asdf","asdf","asdf"};
	if (buf[1] == buf[0])
		throw runtime_error("error!");
    return 0;
}

try语句块

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

给用户的提示信息中输出了err.what()的返回值。err的类型是runtime_error,因此得知what是runtime_error类的一个成员函数。

每个标准库异常类都定义了名为what的成员函数,这些函数没有参数,返回值为“const char*”

【提示】编写异常安全的代码非常困难

我们需要清楚异常何时发生,异常发生后程序如何确保对象有效、资源无泄漏、程序处于合理状态。


标准异常

C++标准库定义了一组类,用于报告标准库函数遇到的问题。这些异常类可以在用户编写的程序中使用。

<exception>定义                           异常类 exception(最通用)       仅支持默认初始化方式

<stdexcept>定义          几种         异常类                                      仅支持string/C风格字符串初始化

<new>定义                                    异常类bad_alloc                     仅支持默认初始化方式

<type_info> 定义                           异常类bad_cast                      仅支持默认初始化方式

异常类型只定义了一个名为what的成员函数,该函数没有任何参数,返回一个指向C风格的字符串。


术语表:

块(block)/复合语句(compound statement):包围在花括号内的由0条或多条语句组成的序列。块也是一条语句,所以只要是能够使用语句的地方,就可以使用块。

异常类(exception class):标准库定义的一组类,用于表示程序发生的错误。

异常声明(exception declaration):位于catch字句中的声明,指定该catch可以处理的类型

带标签语句(labeled statement):前面带有标签的语句。所谓标签是指一个标识符以及紧跟着的一个冒号。对于同一个标识符来说,用作标签的同时还能用于其他目的,互不干扰。

terminate:标准库函数,当异常没有被捕捉到时调用。terminate终止当前程序的执行。

 

猜你喜欢

转载自blog.csdn.net/goldcarpenter/article/details/81941249