面向对象程序设计——Visual C++ 第15章 异常处理
思考题
1、列出五种常用的异常例子。
在C++编程中,常用的异常例子包括:
- 除以零异常:当除数为零时,会抛出除以零异常。
- 空指针异常:当使用空指针访问对象或调用成员函数时,会抛出空指针异常。
- 数组越界异常:当访问数组时,如果下标超出了数组的范围,会抛出数组越界异常。
- 类型转换异常:当进行类型转换时,如果转换失败,会抛出类型转换异常。
- 文件操作异常:当进行文件操作时,如果文件不存在或无法打开,会抛出文件操作异常。
以上异常都是常见的异常,需要在程序中进行处理,以避免程序崩溃或出现不可预料的错误。在C++中,可以使用try-catch语句来捕获和处理异常,从而保证程序的稳定性和可靠性。
2、指出异常处理比传统错误处理方法的优势。
在C++编程中,异常处理比传统错误处理方法具有以下优势:
- 可读性更好:异常处理可以将错误处理代码与正常代码分离,使程序的结构更加清晰,易于阅读和维护。
- 可靠性更高:异常处理可以捕获和处理程序中的异常,避免程序崩溃或出现不可预料的错误,从而提高程序的可靠性和稳定性。
- 灵活性更强:异常处理可以根据不同的异常类型进行不同的处理,从而实现更加灵活的错误处理方式。
- 可扩展性更好:异常处理可以自定义异常类型,并根据不同的异常类型进行不同的处理,从而实现更加灵活和可扩展的错误处理方式。
总的来说,异常处理是一种更加优秀的错误处理方式,可以提高程序的可读性、可靠性、灵活性和可扩展性。在编写程序时,应该尽量使用异常处理来处理错误,以提高程序的质量和可靠性。
3、异常匹配原则是什么?
C++中的异常匹配原则是指当程序抛出异常时,系统会按照一定的规则匹配异常处理程序,以确定哪个处理程序能够处理该异常。匹配原则主要包括以下几个方面:
- 异常类型匹配:处理程序的异常类型必须与抛出的异常类型相匹配,或者是其基类。
- 最近匹配原则:如果有多个处理程序可以匹配抛出的异常,系统会选择最近的处理程序来处理该异常。
- 异常处理程序的顺序:如果有多个处理程序可以匹配抛出的异常,并且它们都与异常类型相匹配,系统会按照处理程序的顺序来选择处理程序。
- 异常处理程序的优先级:如果有多个处理程序可以匹配抛出的异常,并且它们都与异常类型相匹配,系统会按照处理程序的优先级来选择处理程序。
总之,异常匹配原则是为了确保程序能够正确地处理异常,避免异常导致程序崩溃或出现其他问题。
4、比较catch(基类)与catch(...)的异同?
catch(基类)和catch(...)都是用来捕获异常的语句,但是它们有一些异同点:
- 异同点:
- catch(基类)可以捕获指定基类及其派生类的异常,而catch(...)可以捕获任何类型的异常。
- catch(基类)可以在处理异常时使用基类的成员函数和数据成员,而catch(...)只能使用通用的异常处理代码。
- 不同点:
- catch(基类)是一种特定的异常处理方式,需要指定基类类型,而catch(...)是一种通用的异常处理方式,可以捕获任何类型的异常。
- catch(基类)可以提供更精细的异常处理,因为它只捕获指定类型的异常,而catch(...)可能会捕获一些不需要处理的异常。
总之,catch(基类)和catch(...)都有各自的优缺点,需要根据具体情况选择使用哪种方式来处理异常。
习题
2、在程序中用new分配内存空间时,如果操作未成功,则用try语句触发一个字符型常数,用catch语句捕获此异常。试编写一个完整的程序实现这一功能。
以下是一个完整的程序示例,该程序尝试使用 new 运算符分配内存空间,如果操作未成功,则会触发异常并且使用 catch 语句捕获异常。在这个示例中,异常会输出 "Failed to allocate memory!" 信息。
#include <iostream>
using namespace std;
int main() {
try {
int* myArray = new int[1000000000]; // 尝试分配 40 亿个整型数的数组
cout << "Memory allocation succeeded." << endl;
delete[] myArray; // 释放内存
}
catch (const char* msg) {
cerr << msg << endl; // 输出错误信息
}
return 0;
}
在上述代码中,我们使用 try 语句包含了尝试使用 new 运算符分配内存的代码块。如果分配内存操作未成功,则会抛出一个字符型常量 "Failed to allocate memory!"。使用 catch 语句捕获这个异常,并使用 cerr 输出错误信息。
3、定义一个异常类CException,它具有成员函数Reason(),用来显示异常的类型,定义函数fun()触发异常,在主函数的一个try块中调用函数fun(),在相应的catch块中捕获异常,设计一个完整的程序,并观察程序的执行流程。
#include <iostream>
using namespace std;
class CException {
public:
string Reason() {
return "Unknown Exception";
}
};
void fun() {
int* ptr = new int[10000000000000000];
if (!ptr) {
throw CException();
}
}
int main() {
try {
fun();
} catch (CException e) {
cout << "Exception caught: " << e.Reason() << endl;
}
return 0;
}
在上述程序中,我们定义了一个异常类CException,它有一个成员函数Reason()用来返回异常类型的描述。接着我们定义了一个函数fun(),该函数用new分配了一个非常大的内存空间,并检查分配是否成功。如果分配失败,则抛出CException异常。
在主函数中,我们使用try块来调用函数fun(),如果fun()抛出异常,则在相应的catch块中捕获异常并输出异常类型。在上述程序中,由于new分配内存空间非常大,几乎不可能成功,因此fun()总是会抛出异常。当我们运行该程序时,输出如下:
Exception caught: Unknown Exception
可以看到,程序成功捕获了fun()函数抛出的异常,并输出了异常类型描述。