C++ Primer 学习笔记_88_用于大型程序的工具 --异常处理[续1]

用于大型程序的工具

--异常处理[续1]

四、又一次抛出

有可能单个catch不能全然处理一个异常。在进行了一些校正行动之后,catch可能确定该异常必须由函数调用链中更上层的函数来处理,catch能够又一次抛出将异常传递给函数调用链中更上层的函数。又一次抛出是后面不跟类型或表达式的一个throw:

        throw;

空throw语句将又一次抛出异常对象,它仅仅能出如今catch或从catch调用的函数中。假设在处理代码不活动时碰到空throw,就调用terminate函数。

尽管又一次抛出不指定自己的异常,但仍然将一个异常对象沿链向上传递,被抛出的异常是原来的异常对象,而不是catch形參。当catch形參是基类类型的时候,我们不知道由又一次抛出表达式抛出的实际类型,该类型取决于异常对象的动态类型,而不是catch形參的静态类型。比如,来自带基类类型形參catch的又一次抛出,可能实际抛出一个派生类型的对象。

一般而言,catch能够改变它的形參。在改变它的形參之后,假设catch又一次抛出异常,那么仅仅有当异常说明符是引用的时候,才会传播那些改变

    catch (my_error &eObj)
    {
        eObj.status = severeErr;
        throw;	//传播改变
    }
    catch (other_error eObj)
    {
        e.status = badErr;
        throw;	//仅仅是复制对象
    }

五、捕获全部异常的处理代码

能够使用捕获全部异常catch子句来处理不知道确切类型的异常。捕获全部异常的catch子句形式为(...),比如:

    catch (...)
    {
        //...
    }

捕获全部异常的catch子句与随意类型的异常都匹配。

catch(...)常常与又一次抛出表达式结合使用,catch完毕可做的全部局部工作,然后又一次抛出异常:

void manip()
{
    try
    {
        //...
    }
    catch (...)
    {
        //...
        throw;
    }
}

【注解】

假设catch(...)与其它catch子句结合使用,它必须是最后一个,否则,不论什么跟在它后面的catch子句都将不能被匹配。

六、函数測试块与构造函数

在进入构造函数函数体之前处理构造函数初始化式,构造函数函数体内部的catch子句不能处理在处理构造函数初始化式时可能发生的异常。

为了处理来自构造函数初始化式的异常,必须将构造函数编写为函数try块。能够使用函数測试块将一组catch子句与函数联成一个总体。

template <class T> Handle<T>::Handle(T *p)
try :
    ptr(p),use(new size_t(1))
{
    //empty function body
}
catch (const std::bad_alloc &e)
{
    handle_out_of_memory(e);
}

注意:关键词try出如今成员初始化列表之前,而且測试块的复合语句包围了构造函数的函数体。catch子句既能够处理从成员初始化列表中抛出的异常,也能够处理从构造函数函数体中抛出的异常!

因此:构造函数要处理来自构造函数初始化式的异常,唯一方法是将构造函数编写为函数測试块!

//P587 习题17.5
//(a)
class exceptionType {};
throw new exceptionType();
catch(exceptionType *pet)
{
    //...
}

//(b)
throw 8;
catch (...)
{
    //...
}

//(c)
enum mathErr {overflow,underflow,zeroDivide};
throw overflow;
catch (mathErr &ref)
{
    //...
}

//(d)
typedef int EXCEPTYPE;
throw 250;
catch(EXCEPTYPE)
{
    //...
}

七、异常类层次
exception类型所定义的唯一操作是一个名为what的虚成员,该函数返回const char *对象,它一般返回用来在抛出位置构造异常对象的信息。由于what是虚函数,假设捕获了基类类型的引用,对what函数的调用将执行适合异常对象的动态类型的版本号。

1、用于书店应用程序的异常类
应用程序常常通过从exception类或者中间基类派生附加类型来扩充exception层次。这些新派生的类能够表示特定于应用程序领域的异常类型。

标准exception类层次

我们能够定义自己的异常层次来表示可能出现的特定于应用程序的问题。

class out_of_stock : public std::runtime_error
{
public:
    explicit out_of_stock(const std::string &s):
        std::runtime_error(s) {}
};

class isbn_mismatch : public std::logic_error
{
public:
    isbn_mismatch(const std::string &s):std::logic_error(s) {}
    isbn_mismatch(const std::string &s,
                  const std::string &lhs,const std::string &rhs):
        std::logic_error(s),
        left(lhs),right(rhs) {}

    const std::string left,right;
    virtual ~isbn_mismatch() throw() {}
};

通过从标准异常类派生,定义了特定于应用程序的异常类型,能够觉得异常类按层次组织:随着层次的加深,每一层变得更特殊的异常。

第一层即最一般的层由exception类代表,当捕获这一类型的对象时,我们所知道的仅仅是有些地方出错了

第二层将exception特化为两个大类:执行时错误和逻辑错误。我们的书店异常类表示更特化的层中的事件。out_of_stock类表示可能在执行时出现故障的特定于应用程序的事情,能够用它发出不能履行某个订单的信号。isbn_mismatch异常是从logic_error派生的更特殊的异常,原则上,程序能够通过调用same_isbn检測到不匹配的ISBN。

2、使用程序猿定义的异常类型

用和使用标准库类同样的方法使用自己的异常类

Sales_item operator+(const Sales_item &lhs,const Sales_item &rhs)
{
    if (!lhs.same_isbn(rhs))
    {
        throw isbn_mismatch("isbn mismatch",lhs.book(),rhs.book());
    }

    Sales_item ret(lhs);
    ret += rhs;
    return ret;
}

然后,使用加操作符的代码能够检測这个错误,写出适当的错误消息:

    Sales_item item1,item2,sum;
    while (cin >> item1 >> item2)
    {
        try
        {
            sum = item1 + item2;
        }
        catch (const isbn_mismatch &e)
        {
            cerr << e.what() << ": left isbn(" << e.left
                 << "),right isbn(" << e.right << ")"
                 << endl;
        }
    }
时间: 2024-10-10 01:36:29

C++ Primer 学习笔记_88_用于大型程序的工具 --异常处理[续1]的相关文章

C++ Primer 学习笔记_89_用于大型程序的工具 --异常处理[续2]

用于大型程序的工具 --异常处理[续2] 八.自动资源释放 考虑下面函数: void f() { vector<string> v; string s; while (cin >> s) { v.push_back(s); } string *p = new string[v.size()]; //... delete p; } 在正常情况下,数组和vector都在退出函数之前被撤销,函数中最后一个语句释放数组,在函数结束时自动撤销vector. 但是,如果在函数内部发生异常,则将

C++ Primer 学习笔记_90_用于大型程序的工具 --异常处理[续3]

用于大型程序的工具 --异常处理[续3] 九.auto_ptr类[接上] 5.auto_ptr对象的复制和赋值是破坏性操作 auto_ptr和内置指针对待复制和赋值有非常关键的区别.当复制auto_ptr对象或者将它的值赋给其他auto_ptr对象的时候,将基础对象的所有权从原来的auto_ptr对象转给副本,原来的auto_ptr对象重置为未绑定状态. auto_ptr<string> strPtr1(new string("HELLO!")); auto_ptr<

C++ Primer 学习笔记_93_用于大型程序的工具 --命名空间[续2]

用于大型程序的工具 --命名空间[续2] 五.类.命名空间和作用域 名字的可见性穿过任意嵌套作用域,直到引入名字的块的末尾. 对命名空间内部使用的名字的查找遵循常规C++查找规则:当查找名字的时候,通过外围作用域外查找.对命名空间内部使用的名字而言,外围作用域可能是一个或多个嵌套的命名空间,最终以全包围的全局命名空间结束.只考虑已经在使用点之前声明的名字,而该使用仍在开放的块中: namespace A { int i; namespace B { int i; int j; int f1()

C++ Primer 学习笔记_94_用于大型程序的工具 --命名空间[续3]

用于大型程序的工具 --命名空间[续3] 六.重载与命名空间 正如我们所见,每个命名空间维持自己的作用域,因此,作为两个不同命名空间的成员的函数不能互相重载.但是,给定命名空间可以包含一组重载函数成员. 1.候选函数与命名空间 命名空间对函数匹配有两个影响.一个影响是明显的:using声明或using 指示可以将函数加到候选集合.另一个影响则微妙得多. 正如前节所见,有一个或多个类类型形参的函数的名字查找包括定义每个形参类型的命名空间.这个规则还影响怎样确定候选集合,为找候选函数而查找定义形参类

C++ Primer 学习笔记_92_用于大型程序的工具 --命名空间[续1]

用于大型程序的工具 --命名空间[续1] 二.嵌套命名空间 一个嵌套命名空间即是一个嵌套作用域 -- 其作用域嵌套在包含它的命名空间内部.嵌套命名空间中的名字遵循常规规则:外围命名空间中声明的名字被嵌套命名空间中同一名字的声明所屏蔽.嵌套命名空间内部定义的名字局部于该命名空间.外围命名空间之外的代码只能通过限定名引用嵌套命名空间中的名字. 嵌套命名空间可以改进库中代码的组织: namespace cplusplus_primer { namespace QueryLib { class Quer

C++ Primer 学习笔记_88_用于大型程序的工具 -错误处理[续1]

用于大型程序的工具 --异常处理[续1] 四.重新抛出 有可能单个catch不能完全处理一个异常.在进行了一些校正行动之后,catch可能确定该异常必须由函数调用链中更上层的函数来处理,catch可以重新抛出将异常传递给函数调用链中更上层的函数.重新抛出是后面不跟类型或表达式的一个throw: throw; 空throw语句将重新抛出异常对象,它只能出现在catch或从catch调用的函数中.如果在处理代码不活动时碰到空throw,就调用terminate函数. 虽然重新抛出不指定自己的异常,但

C++ Primer 学习笔记_87_用于大型程序的工具 --异常处理

用于大型程序的工具 --异常处理 引言: C++语言包括的一些特征在问题比較复杂,非个人所能管理时最为实用.如:异常处理.命名空间和多重继承. 相对于小的程序猿团队所能开发的系统需求而言,大规模编程[往往涉及数千万行代码]对程序设计语言的要求更高.大规模应用程序往往具有下列特殊要求: 1.更严格的正常运转时间以及更健壮的错误检測和错误处理.错误处理常常必须跨越独立开发的多个子系统进行[异常处理]. 2.能够用各种库(可能包括独立开发的库)构造程序[命名空间]. 3.能够处理更复杂的应用概念[多重

C++ Primer 学习笔记_95_用于大型程序的工具 --多重继承与虚继承

用于大型程序的工具 --多重继承与虚继承 引言: 大多数应用程序使用单个基类的公用继承,但是,在某些情况下,单继承是不够用的,因为可能无法为问题域建模,或者会对模型带来不必要的复杂性. 在这些情况下,多重继承可以更直接地为应用程序建模.多重继承是从多于一个直接基类派生类的能力,多重继承的派生类继承其所有父类的属性. 一.多重继承 1.定义多个类 为了支持多重继承,扩充派生列表: class Bear : public ZooAnimal { //... }; 以支持由逗号分隔的基类列表: cla

C++ Primer 学习笔记_96_用于大型程序的工具 --多重继承与虚继承[续1]

用于大型程序的工具 --多重继承与虚继承[续1] 四.多重继承下的类作用域 成员函数中使用的名字和查找首先在函数本身进行,如果不能在本地找到名字,就继续在本类中查找,然后依次查找每个基类.在多重继承下,查找同时检察所有的基类继承子树 -- 在我们的例子中,并行查找 Endangered子树和Bear/ZooAnimal子树.如果在多个子树中找到该名字,则那个名字的使用必须显式指定使用哪个基类;否则,该名字的使用是二义性的. [小心地雷] 当一个类有多个基类的时候,通过对所有直接基类同时进行名字查