面向对象程序设计及C++实验指导(第3版)
上QQ阅读APP看书,第一时间看更新

第一部分 教材思考题解析

第2章 C++对C的改进及扩展

1.例2-4的思考题:

①在程序的第10行之后增加一条语句“cout<<i;”,重新编译链接程序,有什么现象?请解释原因。

②将程序中的第8行注释掉,即删除局部变量sum的定义语句,其余代码不变,程序运行结果是什么?请解释原因。

③恢复第8行,即保留局部变量sum的定义语句,然后将第3行注释掉,即删除全局变量sum的定义,重新编译程序,会有怎样的提示?请解释原因。

【分析与解答】①重新编译链接程序,在新增的这一行会有出错提示“error C2065:“i”:未声明的标识符”。这是因为,i变量是在for语句中定义的,其作用域仅限于for语句,也就是在第10行之后增加的语句中涉及的变量i就超出了第9行定义的i的作用域了,所以被系统认为是未声明的标识符。

②删除局部变量sum的定义,重新编译链接程序,正确,运行结果如下。

因为程序中只有全局变量,程序中sum和::sum表达的都是全局变量,所以3个元素的和值80累加到sum中,5050+80的结果为5130,所以第1行的结果是5130,至于提示信息还是按原来的。再执行语句“::sum+=sum;”,这两种形式的sum都是全局变量,于是5130被加到5130的sum中得到了10260,是5130的两倍,最后一行输出的结果就是10260。

③保留局部变量sum的定义语句,同时删除全局变量sum的定义,重新编译程序,会有两个报错,分别定位于第12行和第13行,错误信息均为“error C2039:“sum”不是“global namespace”的成员”,由此可见,对于局部变量sum,其前面不可以加域解析符“::”表示。

2.例2-5的思考题:

①将第4行改为“void Fun(int i,int j,int k);”,同时将第13行改为“void Fun(int i,int j=5,int k=10);”,编译链接程序有什么现象?请解释原因。

②恢复第4行和第13行,将第10行分别改为“Fun();”和“Fun(20,,40);”,观察编译结果。

③还原第10行,将第4行改为“void Fun(int i,int j=5,int k);”,观察编译结果。

【分析与解答】①将第4行改为“void Fun(int i,int j,int k);”,同时将第13行改为“void Fun(int i,int j=5,int k=10);”,编译链接程序会有报错,显示的出错位置在源程序的第8行和第9行,分别显示“error C2660:“Fun”:函数不接受1个参数”和“error C2660:“Fun”:函数不接受2个参数”。其根本原因是在函数声明中删去了默认参数值,而在后面定义的首部再给默认参数系统不认。所以,默认参数值的给定需在函数的声明里(如果函数先声明后定义)或者在函数首部(函数直接定义)。

②恢复第4行和第13行,将第10行改为“Fun();”,编译后给出一个报错“error C2660:“Fun”:函数不接受0个参数”,这是因为函数只提供了后两个形参的默认值,第一个形参没有默认值,所以需要在调用的时候提供实际参数(简称实参),该函数的调用至少需要一个实参;如果将第10行改为“Fun(20,,40);”,则编译的时候报一个错“error C2059:语法错误:“,””,这是因为提供实参时应当从左到右,中间不能有间隔。相应地,提供默认参数值时要求从右到左,中间没有间隔。

③还原第10行,将第4行改为“void Fun(int i,int j=5,int k);”,则编译的时候报一个错“error C2548:“Fun”:缺少参数3的默认参数”,这是因为,提供默认参数值时要求依次从右到左,所以只有在给定了第3个参数的默认值的前提下,才可以给第2个参数的默认值。

3.例2-6的思考题:

①将第4行的“int square(int x)”改为“int square(int x=100)”,其余代码不变,重新编译链接程序,会有什么现象?请解释原因。

②将第4行的“int square(int x)”改为“int square(int x,int y=1)”,同时将第6行代码改为“{return x*x+y*y;}”,重新运行程序,会有怎样的结果?请解释原因。

【分析与解答】①将第4行的“int square(int x)”改为“int square(int x=100)”,其余代码不变,重新编译链接程序,则有一个报错“error C2668:“square”:对重载函数的调用不明确”,就是代码第18行的square()函数调用不明确,因为这样修改之后,重载函数的第一个版本“int square(int x=100)”和第三个版本“double square(double x=1.5)”都带有默认参数值,都可以不提供实参,从而使square()函数无法确定调用哪个版本的函数。

②将第4行的“int square(int x)”改为“int square(int x,int y=1)”,同时将第6行代码改为“{return x*x+y*y;}”,重新运行程序,则输出结果如下。

可以看到,第2行的输出结果为101,函数square(10)调用的是第一个版本的函数,第二参数用的是默认参数值,该调用无歧义。

由以上两种修改导致的不同结果说明,重载函数结合默认参数值,只要保证每一次函数调用能匹配到唯一的一个重载函数版本就是正确的,不允许调用有歧义。

4.例2-10的思考题:

①将第11行的“sum=new int(0)”改为“sum=new int”,其余代码不变,重新编译链接、运行程序,会有什么现象?请解释原因。

②将第11行的代码恢复成“sum=new int(0)”,同时将第18行的“(*sum)++”改为“*sum++”,其余代码不变,重新编译链接、运行程序,会有什么现象?请解释原因。

【分析与解答】①将第11行的“sum=new int(0)”改为“sum=new int”,其余代码不变,重新编译链接程序,无报错;运行程序,则显示的奇数个数与实际统计结果不吻合,是一个很大的负数。原因就在于,改为“sum=new int”之后,动态空间*sum的初值为一个随机值,而未修改前“sum=new int(0);”是在申请了一个int型的动态空间*sum之后立即对*sum初始化为0,所以保证了统计结果的正确性。因此在申请完动态空间后要做及时的赋值或读入处理,保证获得有意义的值。

②将第11行的代码恢复成“sum=new int(0)”,同时将第23行代码“(*sum)++”改为“*sum++”,其余代码不变,重新编译链接程序,无报错;运行程序,则显示的奇数个数与实际统计结果不吻合,看不出什么意义,在输出结果之后弹出一个意外终止框,原因就在于,原来的代码“(*sum)++”就是指*sum自增1,这是没有问题的。而改为“*sum++”后,根据单目运算符右结合的特性,先执行sum++,指针自增1,即向后移动4个字节,然后再用*sum访问当前值,所以,如果有15个奇数,则sum++执行了15次,最终sum指针所指向的内存空间就不是当初用new申请到的空间了,对这个空间用delete释放必然出错,而且这个未知空间中的值肯定不会是实际奇数的个数,所以输出结果无意义。

5.例2-11的思考题:

①删除代码的第12行、第19行、第22行,也就是不用try-catch进行异常处理。重新编译链接、运行程序,会有什么现象?请解释原因。

②代码恢复原样,然后将第16行代码中的“divide(a,c)”改为“divide(a,c+1)”,其余代码不变,重新运行程序,会有什么现象?

【分析与解答】①删除代码的第12行、第19行、第22行,重新编译链接程序,无报错。重新运行程序,则输出前两行。

然后弹出一个意外终止框,程序运行停止。这是因为第三次调用的时候出现了除数为0的现象,这是无法正常运算的,在未启用try-catch进行异常处理的情况下,只能意外终止,影响了程序的健壮性。由此也可以看到异常检测及处理的必要性。

②代码恢复原样,然后,将第16行代码中的“divide(a,c)”改为“divide(a,c+1)”,其余代码不变,重新编译链接程序,无报错。重新运行程序,输出结果如下。

显然try块中检测的4条语句都没有在divide函数中通过throw抛出异常,于是main()函数的catch块中的语句都没有被执行,程序无任何异常,因此输出4个除法结果后接着输出catch块后的输出内容calculate finished,程序正常结束。可见,在没有异常发生时catch块中的内容不执行,不影响正常情况下的输出结果。