练习18.1 在下列throw语句中异常对象的类型是什么?如果将(b)中的throw语句写成了throw p将发生什么情况?
(a) range_error("error");
throw r;
(b) exception *p = &r;
throw *p;
a和b中的异常对象都是range_error。
throw p找不到匹配的catch对象,调用terminate,程序终止。
练习18.2 当在指定的位置发生了异常时将出现什么情况?
void exercise(int *b, int *e)
{
vector<int> v(b, e);
int *p = new int[v.size()];
ifstream in("ints");
//此处发生异常
}
v和in调用自己的析构函数释放内存,而只能指针p指向的内存丢失。
练习18.3 要想让上面的代码在发生异常时能够正常工作,有两种解决方案。请描述这两种解决方法并实现他们。
(a) 使用智能指针
std::shared_ptr<int> p(new int[v.size()], [](int *p) {delete p});
(b) 创建类对象 P p(v.size())
struct P {
int *p = nullptr;
P(std::size_t n):p(new int[n]){}
~P()
{
delete []p;
}
};
练习18.4 查看图18.1(第693页)所有的继承体系,说明下面的try块有何错误并修改它。
如果在多个catch语句的类型之间存在着继承关系,则我们应该把继承链最底端的类放在前面,而将继承链最顶端的类放在后面。
try {
//...
}
catch (overflow_error eboj) {
//...
}
catch (const runtime_error &re) {
//...
}
catch (exception) {
//...
}
练习18.5 修改下面的main函数,使其能捕获图18.1(第693页)所示的任何异常类型:
int main() {
// 使用C++标准库
}
这段代码应该首先打印异常相关的错误信息,然后调用abort(定义在cstdlib头文件中)终止main函数。
#include <iostream>
#include <exception>
#include <cstdlib>
using namespace std;
int main()
{
try {
//...
}
catch (range_error &r) {
cout << r.what();
abort();
}
catch (underflow_error &r) {
cout << r.what();
abort();
}
catch (overflow_error &r) {
cout << r.what();
abort();
}
catch (length_error &r) {
cout << r.what();
abort();
}
catch (out_of_range &r) {
cout << r.what();
abort();
}
catch (invalid_argument &r) {
cout << r.what();
abort();
}
catch (domain_error &r) {
cout << r.what();
abort();
}
catch (runtime_error &r) {
cout << r.what();
abort();
}
catch (logic_error &r) {
cout << r.what();
abort();
}
catch (bad_cast &r) {
cout << r.what();
abort();
}
catch (bad_alloc &r) {
cout << r.what();
abort();
}
catch (exception r) {
cout << r.what();
abort();
}
}
练习18.6 已知下面的异常类型和catch语句,书写一个throw表达式使其创建的异常对象能被这些catch语句捕获:
(a) class exceptionType{};
catch (exceptionType *pet){}
(b) catch (...) {
}
(c) typedef int EXCEPTYPE;
catch(EXCEPTYPE){}
(a) throw &exceptionType();
(b) 可以捕获任何异常类型
(c) throw EXCPETYPE;
练习18.7 根据第16章的介绍定义你自己的Blob和BlobPtr,注意将构造函数写成函数try语句块。
template <typename T>
Blob<T>::Blob()try :data(std::make_shared<vector<T>()>) {}
catch(const std::bad_alloc &e){
handle_out_of_memory(e);
}
template<typename T>
Blob<T>::Blob(std::initializer_list<T> il) try :data(make_shared<vector<T>>(il)) {}
catch(const std::bad_alloc &e){
handle_out_of_memory(e);
}
template <typename T>
BlobPtr<T>::BlobPtr()try:curr(0){}
catch (const std::bad_alloc& e) {
handle_out_of_memory(e);
}
template <typename T>
BlobPtr<T>::BlobPtr(Blob<T> &a, size_t sz = 0)try : wptr(a.data), curr(sz) {}
catch (const std::bad_alloc& e) {
handle_out_of_momory(e);
}
练习18.8 //...
练习18.9 定义本节描述的书店程序异常,然后为Sales_data类重新编写一个复合赋值运算符并令其抛出一个异常。
#include "Sales_data.h"
int main()
{
Sales_data item1, item2;
while (cin >> item1 >> item2) {
try {
Sales_data sum = item1 + item2;
}
catch (const isbn_mismatch &e) {
cerr << e.what() << ": left isbn(" << e.left << ") right isbn(" << e.right << ")" << endl;
}
}
}
练习18.10 编写程序令其对两个ISBN编号不相同的对象执行Sales_data的加法运算。为该程序编写两个不同的版本:一个处理异常,另一个不处理。观察并比较这两个程序的行为,用心体会当出现了一个未被捕获的异常处理时程序会发生什么情况。
版本异常处理如上一题。
版本为异常处理:
#include "Sales_data.h"
int main()
{
Sales_data item1("C++", 1, 89.9);
Sales_data item2("C", 1, 49.5);
Sales_data sum = item1 + item2;
}
运行时将调用terminate。
练习18.11为什么what函数不应抛出异常。
如果what函数抛出异常,那么就要有函数try语句块,函数中再调用what函数...无穷无尽。