18.1
异常对象的类型:
(a):range_error
(b):range_error
throw p将抛出一个exception类型的异常
18.2
分配的动态内存将无法释放
答案:发生异常,所在块之前的临时变量皆会被销毁,V会调用vector类的析构函数进行销毁,并释放相关内存,P指针会被销毁,但是P指针指向的内存由于是动态分配的,所以该内存不会被释放,造成内存泄漏。输入流对象会调用ifstream类的析构函数销毁,最后程序被终止。
18.3
知识点:类对象分配的资源将由类的析构函数负责释放。因此如果我们使用类来控制资源的分配,就能确保无论函数正常结束还是遭遇异常,资源都能被正确地释放。
方一:使用智能指针,并传入删除的lambda表达式
shared_ptr<int> p(new int[v.size()], [](int *p){ delete[] p; });//lambda表达式相当于一个删除器
方二:使用类,创建一个包含一个int*的类,在析构函数进行delete文件指针
class intAr { int *p=nullptr; public: intAr(size_t n): p(new int[n]){} ~intAr() { delete []p; } }
18.4
错误:这几个catch语句的类型之间存在继承关系,而它把上层继承链的类放在下层继承链的类上面,这样会导致匹配准确度下降。
正确的做法:将派生类异常的处理代码出现在基类异常的处理代码之前
try { // 使用C++标准库 } catch(overflow_error eobj) { //... } catch(const runtime_error &re) { //... } catch(exception) { //... }
18.5
int main() { try { throw runtime_error("error!"); //各种异常 } catch (overflow_error e) { cout << e.what(); abort(); } catch (underflow_error u) { cout << u.what(); abort(); } catch (range_error r) { cout << r.what(); abort(); } catch (domain_error d) { cout << d.what(); abort(); } catch (invalid_argument i) { cout << i.what(); abort(); } catch (out_of_range o) { cout << o.what(); abort(); } catch (length_error l) { cout << l.what(); abort(); } catch (runtime_error r) { cout << r.what(); abort(); } catch (logic_error l) { cout << l.what(); abort(); } catch (bad_alloc b) { cout << b.what(); abort(); } catch (exception e) { cout << e.what(); abort(); } catch (...) { abort(); } return 0; }
18.6
(a):throw &exceptionType();
(b):throw 任意异常类型();
(c):throw int();
18.7
template<typename T> Blob<T>::Blob() try : data(make_shared<vector<T>>()) { } catch (const bad_alloc &e) { cerr << e.what() << endl; } template<typename T> Blob<T>::Blob(initializer_list<T> il) try : data(make_shared<vector<T>>(il)) { } catch (const bad_alloc &e) { cerr << e.what() << endl; }
18.9
class run_err : public runtime_error { public: explicit run_err(const string &s): runtime_error(s) {} //调用runtime_error的构造函数 }; class isbn_nomatch : public logic_error { public: explicit isbn_nomatch(const string &s): logic_error(s) {} isbn_nomatch(const string &s, const string &lhs, const string &rhs): logic_error(s), left(lhs), right(rhs) {} const string left, right; }; Sales_data& Sales_data::operator+=(const Sales_data &rhs) { if (isbn() != rhs.isbn()) throw isbn_nomatch("wrong isbns", isbn(), rhs.isbn()); units_sold += rhs.units_sold; revenue += rhs.revenue; return *this; }
18.10
/* Sales_data.h */ #include <iostream> #include <fstream> #include <string> #include <iomanip> #include <cstdlib> #include <stdexcept> #include <exception> #include <new> using namespace std; class run_err : public runtime_error { public: explicit run_err(const string &s): runtime_error(s) {} //调用runtime_error的构造函数 }; class isbn_nomatch : public logic_error { public: explicit isbn_nomatch(const string &s): logic_error(s) {} isbn_nomatch(const string &s, const string &lhs, const string &rhs): logic_error(s), left(lhs), right(rhs) {} const string left, right; }; class Sales_data { std::string bookNo; unsigned units_sold = 0; double revenue = 0.0; double avg_price() const; public: Sales_data() = default; Sales_data(const std::string &s) : bookNo(s) {} Sales_data(const std::string &s, unsigned n, double p) : bookNo(s), units_sold(n), revenue(p*n) {} explicit Sales_data(std::istream &is) { is >> *this; } std::string isbn() const { return bookNo; } Sales_data& operator+=(const Sales_data&); friend std::ostream &operator<<(std::ostream&, const Sales_data&); friend std::istream &operator>>(std::istream&, Sales_data&); friend Sales_data operator+(const Sales_data&, const Sales_data&); friend bool operator==(const Sales_data&, const Sales_data&); friend bool operator!=(const Sales_data&, const Sales_data&); }; Sales_data& Sales_data::operator+=(const Sales_data &rhs) { if (isbn() != rhs.isbn()) throw isbn_nomatch("wrong isbns", isbn(), rhs.isbn()); units_sold += rhs.units_sold; revenue += rhs.revenue; return *this; } Sales_data operator+(const Sales_data &lhs, const Sales_data &rhs) { Sales_data sum = lhs; sum += rhs; return sum; } inline double Sales_data::avg_price() const { if (units_sold != 0) return revenue / units_sold; else return revenue; } std::ostream& operator<<(std::ostream& os, const Sales_data& item) { os << item.bookNo << " " << item.units_sold << " " << item.revenue << " " << item.avg_price(); return os; } std::istream& operator>>(std::istream &is, Sales_data& item) { double price = 0; is >> item.bookNo >> item.units_sold >> price; if (is) item.revenue = item.units_sold * price; else item = Sales_data(); return is; } bool operator==(const Sales_data &lhs, const Sales_data &rhs) { return lhs.bookNo == rhs.bookNo && lhs.units_sold == rhs.units_sold && lhs.revenue == rhs.revenue; } bool operator!=(const Sales_data &lhs, const Sales_data &rhs) { return !(lhs == rhs); }
不处理异常版本(未捕获的异常会调用terminate,导致程序结束):
#include <iostream> #include "Sales_data.h" using namespace std; int main() { Sales_data item1("book1",10,23.3), item2("book2",6,66.6), sum; cout << item1 + item2; return 0; }
处理异常版本:
#include <iostream> #include "Sales_data.h" using namespace std; int main() { Sales_data item1, item2, sum; while (cin >> item1 >> item2) { try { sum = item1 + item2; } catch (const isbn_nomatch &e) { cout << e.what() << ": left isbn(" << e.left << ") right isbn(" << e.right << ")" << endl; } cout << "book: " << sum << endl; } return 0; }
18.11
what函数返回用于初始化异常对象的信息,如果它也能抛出异常,那将会是无限循环。
18.13
使用未命名的命名空间:其中定义的名字对于整个文件有效
18.14
mathLib::MatrixLib::matrix mathLib::MatrixLib::matrix::operator*(const matrix&, const matrix&);
18.15
using声明:一次只引入命名空间的一个成员;它引入的名字的有效范围为从using声明的地方开始,一直到using声明所在的作用域结束(即using声明的名字的作用域与using声明语句本身的作用域一致);一条using声明语句可以出现在全局作用域、局部作用域、命名空间作用域及类的作用域中。
using指示:使得某个特定的命名空间中所有的名字都可见;它将命名空间成员提升到包含命名空间本身和using指示的最近作用域;它不能出现在类的作用域中。
18.16
对命名空间Exercise中所有成员的using声明
出现在位置1:++ivar出现二义性 报错,ivar已经存在
出现在位置2:++ivar将自增命名空间内的ivar成员 报错,dvar重新定义
18.17
测试结果:修改见上题
#include <iostream> using namespace std; namespace Exercise { int ivar = 0; double dvar = 0; const int limit = 1000; } int ivar = 0; using Exercise::ivar; //[Error] 'ivar' is already declared in this scope using Exercise::dvar; using Exercise::limit; void manip() { double dvar = 3.1416; int iboj = limit + 1; ++ivar; ++::ivar; } int main() { manip(); return 0; }
#include <iostream> using namespace std; namespace Exercise { int ivar = 0; double dvar = 0; const int limit = 1000; } int ivar = 0; void manip() { using Exercise::ivar; using Exercise::dvar; using Exercise::limit; double dvar = 3.1416; //[Error] redeclaration of 'double dvar' int iboj = limit + 1; ++ivar; ++::ivar; } int main() { manip(); return 0; }
18.18
存在语句using std::swap;,因此在当前作用域内出现的swap都是命名空间std的成员。
mem1是一个int时:在命名空间std中查找(交换两个对象的模板)swap函数,并实例化该模板
mem1是一个string时,mem1是一个类类型的对象,于是查找实参的类(也就是string类)所属的命名空间(即std),在std中,查找到string类的swap函数
补:
当meml是string时:
当前作用域-找到std::swap-参数string-使用string类的swap
当meml是int时:
当前作用域-找到std::swap,实例化出int型,结束
18.19
使用std的swap,因为限定了是使用std的,所以string版本就直接在std里寻找,int也是直接实例化int的swap版本
18.21
(a):正确,定义一个派生类CADVehicle,它公有继承CAD,私有继承Vehicle
(b):错误,在某个给定的派生列表中,同一个基类只能出现一次
(c):正确,定义一个派生类iostream,公有继承istream和ostream
18.22
先构造mi的C部分,再构造mi的Z部分,最后构造mi的MI成员。
其中,构造mi的C部分又包括:先构造mi的A部分,再构造mi的B部分,最后构造mi的C成员;而构造mi的Z部分又包括:先构造mi的X部分,再构造mi的Y部分,最后构造mi的Z成员。
18.23
都允许,因为我们可以令某个可访问基类的指针或引用直接指向一个派生类对象。
class A { public: A() { cout << "A()" << endl; } }; class B : public A { public: B() : A() { cout << "B()" << endl; } }; class C : public B { public: C() : B() { cout << "C()" << endl; } }; class X { public: X() { cout << "X()" << endl; } }; class D : public X, public C { public: D() { cout << "D()" << endl; } }; int main() { D *pd = new D; X *px = pd; //X是D的基类 A *pa = pd; //A是D的(间接)基类 B *pb = pd; C *pc = pd; delete pd; return 0; }
18.24
ZooAnimal *pb = new Panda("ying_yang"); pb->print(); //正确:Panda::print() pb->cuddle(); //错误:不属于ZooAnimal的接口 pb->highlight(); //错误:不属于ZooAnimal的接口 delete pb; //正确:Panda::~Panda()
18.25
(a):调用Base1的print()
(b):调用D1的print()
(c):调用D2的print()
(d):调用Base2的虚析构函数
(e):调用D1的虚析构函数
(f ):调用D2的虚析构函数
18.26
因为MI从它的两个基类中继承了同名成员print,即使它们的形参类型不同,但编译器先查找名字后进行类型检查,即调用print时,编译器发现多个print,于是报错。
因为MI类中定义了print成员,这将隐藏继承的print成员,故找不到接受int的print
struct MI : public Derived, public Base2 { void print(vector<double>); void print(int); protected: int *ival; vector<double> dvec; }; void MI::print(int i) { Base2::print(i); }
18.27
(a):Base1的非私有成员、Base2的非私有成员、Derived的非私有成员、MI的所有成员
(b):print、dval
(c):dval = Base1::dval + Derived::dval;
(d):Base2::fval = dvec.back();
(e):sval[0] = Base1::cval; Derived::sval[0] = Base1::cval;
18.28
直接访问(意义明确):bar、ival
需限定符(有二义性):foo、cval
18.29
(a):构造函数(先构造虚基类Base,再按声明的顺序逐一构造其他非虚基类Class、D1、D2、MI、Class),析构函数(与构造顺序相反)
(b):一个Base部分,两个Class部分(但是独立的,其中一个是Final对象的MI部分的Class部分,另一个是Final对象的Cass部分)
(c):错误的有(a)、(c)
18.30
class Class {}; class Base : public Class { public: Base(): Class(), ival(0) {}; Base(const Base &b) = default; Base(int a): Class(), ival(a) {} protected: int ival; }; class D1 : public virtual Base { public: D1(): Base() {} D1(const D1 &b) = default; D1(int a): Base(a) {} }; class D2 : public virtual Base { public: D2(): Base() {} D2(const D2 &b) = default; D2(int a): Base(a) {} }; class MI : public D1, public D2 { public: MI() {} MI(const MI &m): Base(m), D1(m), D2(m) {} MI(int i): Base(i), D1(i), D2(i) {} }; class Final : public MI, public Class { public: Final() {} Final(const Final &f) : Base(f), MI(f), Class() {} Final(int i): Base(i), Class() {} };