面向对象程序设计——Visual C++ 第15章 异常处理

思考题

1、列出五种常用的异常例子。

在C++编程中,常用的异常例子包括:

  1. 除以零异常:当除数为零时,会抛出除以零异常。
  2. 空指针异常:当使用空指针访问对象或调用成员函数时,会抛出空指针异常。
  3. 数组越界异常:当访问数组时,如果下标超出了数组的范围,会抛出数组越界异常。
  4. 类型转换异常:当进行类型转换时,如果转换失败,会抛出类型转换异常。
  5. 文件操作异常:当进行文件操作时,如果文件不存在或无法打开,会抛出文件操作异常。

以上异常都是常见的异常,需要在程序中进行处理,以避免程序崩溃或出现不可预料的错误。在C++中,可以使用try-catch语句来捕获和处理异常,从而保证程序的稳定性和可靠性。

2、指出异常处理比传统错误处理方法的优势。

在C++编程中,异常处理比传统错误处理方法具有以下优势:

  1. 可读性更好:异常处理可以将错误处理代码与正常代码分离,使程序的结构更加清晰,易于阅读和维护。
  2. 可靠性更高:异常处理可以捕获和处理程序中的异常,避免程序崩溃或出现不可预料的错误,从而提高程序的可靠性和稳定性。
  3. 灵活性更强:异常处理可以根据不同的异常类型进行不同的处理,从而实现更加灵活的错误处理方式。
  4. 可扩展性更好:异常处理可以自定义异常类型,并根据不同的异常类型进行不同的处理,从而实现更加灵活和可扩展的错误处理方式。

总的来说,异常处理是一种更加优秀的错误处理方式,可以提高程序的可读性、可靠性、灵活性和可扩展性。在编写程序时,应该尽量使用异常处理来处理错误,以提高程序的质量和可靠性。

3、异常匹配原则是什么?

C++中的异常匹配原则是指当程序抛出异常时,系统会按照一定的规则匹配异常处理程序,以确定哪个处理程序能够处理该异常。匹配原则主要包括以下几个方面:

  1. 异常类型匹配:处理程序的异常类型必须与抛出的异常类型相匹配,或者是其基类。
  2. 最近匹配原则:如果有多个处理程序可以匹配抛出的异常,系统会选择最近的处理程序来处理该异常。
  3. 异常处理程序的顺序:如果有多个处理程序可以匹配抛出的异常,并且它们都与异常类型相匹配,系统会按照处理程序的顺序来选择处理程序。
  4. 异常处理程序的优先级:如果有多个处理程序可以匹配抛出的异常,并且它们都与异常类型相匹配,系统会按照处理程序的优先级来选择处理程序。

总之,异常匹配原则是为了确保程序能够正确地处理异常,避免异常导致程序崩溃或出现其他问题。

4、比较catch(基类)与catch(...)的异同?

catch(基类)和catch(...)都是用来捕获异常的语句,但是它们有一些异同点:

  1. 异同点:
  • catch(基类)可以捕获指定基类及其派生类的异常,而catch(...)可以捕获任何类型的异常。
  • catch(基类)可以在处理异常时使用基类的成员函数和数据成员,而catch(...)只能使用通用的异常处理代码。
  1. 不同点:
  • 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()函数抛出的异常,并输出了异常类型描述。