C++ Primer 中文版(第5版) 习题答案

来源:互联网 发布:食品零售数据 编辑:程序博客网 时间:2024/05/16 05:35

      • 第13章 拷贝控制
      • 第18章 用于大型程序的工具
      • 第19章 特殊工具与技术

第13章 拷贝控制

第18章 用于大型程序的工具

Date: 2017-10-08
练习18.1
(a) range_error; (b) range_error;
改写后,将发生异常且程序中止。因为抛出异常时,当前块中创建的指针对象p会被销毁,而p指向的动态内存尚未被释放,造成内存泄漏。

练习18.2
异常发生后,v调用vector类的析构函数销毁,p指针被销毁但指向的内存不会被释放,输入流对象in调用ifstream类的析构函数销毁。

练习18.3
方案一、智能指针

shared_ptr<int> p(new int[v.size()], [](int *p){ delete[] p; });

方案二、类

class intPtr {private:    int *p = nullptr;public:    intPtr(size_t n): p(new int[n]) {}    ~intPtr() { delete[] p; }};

练习18.4
三个类的顺序反序即可

练习18.5

#include <iostream>    #include <cstdlib>   using namespace std;  int main() {        try {    //    }        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 (runtime_error r) {            cout << r.what();            abort();        }    // end of one inheritance    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 (logic_error l) {            cout << l.what();            abort();        }    // end of one inheritance    catch (bad_alloc b) {            cout << b.what();            abort();        }    // end of one inheritance        catch (bad_alloc b) {            cout << b.what();            abort();        }    // end of one inheritance    catch (exception e) {            cout << e.what();            abort();        }    // end of all inheritance    return 0;    }   

练习18.6
(a) throw &exceptionType();
(b) throw int() //任意的
(c) throw int()

练习18.7
修改构造函数为

template<typename T>Blob<T>::Blob() try:data(std::make_shared<std::vector<T>>()) { }catch(std::bad_alloc &e) {    std::cerr << e.what() << std::endl;}

练习18.8
自行修改

练习18.9
异常类如下

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

重载运算符如下

Sales_data& Sales_data::operator+=(const Sales_data& rhs) {    if (isbn() != rhs.isbn())         throw isbn_mismatch("wrong isbn", isbn(), rhs.isbn());    units_sold += rhs.units_sold;    revenue += rhs.revenue;    return *this;}

练习18.10
不捕获异常将中止程序运行

练习18.11
what函数是捕获异常后用来打印异常信息的。即使真的要求what函数抛出异常,也必须在函数体内捕获该异常并处理。

练习18.12
自行修改

练习18.13
当需要定义全局静态变量或局部静态变量时。
换言之,当需要定义的对象、函数、类类型或其他实体在一个作用域中可见时。

练习18.14

mathLib::MatrixLib::matrixmathLib::MatrixLib::operator*( const mathLib::MatrixLib::matrix&,const mathLib::MatrixLib::matrix&);

练习18.15
using指示一次性注入某个命名空间中的所有名字,using声明只注入某个命名空间中的一个名字。

练习18.16
位置1,using声明:
第二个ival将报错declaration already in scope,且内部dvar将隐藏外部dvar.
位置2,using声明:
内部dvar将报错declaration already in scope,且内部ivar将隐藏外部ivar.
位置1,using指示:
++ival处报错Reference to 'ivar' is ambiguous,且内部dvar将隐藏外部dvar.
位置2,using指示:
++ival处报错Reference to 'ivar' is ambiguous,且内部dvar将隐藏外部dvar.
一个有趣现象是,如果ival在manip()内部声明,则无论using指示位于函数内还是函数外,均不会产生错误。且编译器将选用内部ival的声明。

练习18.17
如上

练习18.18
mem1是string时,会调用string类的swap函数;mem1是int时,会调用std的swap函数。

练习18.19
如果using std::swap(v1.mem1, v2.mem1),则只能调用std的swap函数。

练习18.20
候选函数集:
void primerLib::compute();
void primerLib:compute(const void*);
void compute(int);
void compute(double, double = 3.4);
void compute(char*, char* = 0);
可行函数集:
void primerLib:compute(const void*);
void compute(int);
void compute(double, double = 3.4);
void compute(char*, char* = 0);
最佳匹配:
void compute(int);

如果将using声明置于f函数中,
候选函数集:
void primerLib::compute();
void primerLib:compute(const void*);
可行函数集:
void primerLib:compute(const void*);
最佳匹配:
void primerLib:compute(const void*);

练习18.21
(a)无错误;
(b)错误,继承列表不可重复;
(c)无错误

练习18.22
执行顺序:A - B - C - X - Y - Z - MI

练习18.23
均允许

练习18.24

ZooAnimal *pz = new Panda("ying_yang");pz -> print();          // 正确,Panda::print()pz -> cuddle();         // 错误,不属于ZooAnimal的接口pz -> highlight();      // 错误,不属于ZooAnimal的接口delete pz;              // 正确,Panda::~Panda()

练习18.25
(a) MI::print()
(b) MI::print()
(c) MI::print()
(d) ~MI(); ~D2(); ~Base2(); D1(); Base1();
(e) ~MI(); ~D2(); ~Base2(); D1(); Base1();
(f) ~MI(); ~D2(); ~Base2(); D1(); Base1();

练习18.26
产生二义性(即使参数列表不同)。修改为
mi.Base1::print(42);

练习18.27
(a) 可见的名字有
Base1: ival, dval, cval, print();
Base2: fval, print();
Derived: dval, print(), sval;
MI: ival, print(), dvec, foo();
foo: dval.
(b) 是
ival: Base1, MI;
dval: Base1, Derived;
print: Base1, Base2, Derived, MI.
(c) 见下
(d) 见下
(e) 见下

void MI::foo(double cval) {    int dval;    dval = Base1::dval + Deriverd::dval;    Base2::fval = dvec.back();    sval[0] = Base1::cval;}

练习18.28
无须访问限定符:Derived1::Bar(char); Derived2::ival;
必须有限定符:Base::bar(int); Base::ival; foo(); cval;

练习18.29
(a) 构造顺序: Class - Base - D1 - D2 - MI - Class - Final
析构顺序:相反
(b) 1个Base, 2个Class
(c) ac错误,bd正确。指向基类的指针或者引用可以直接指向派生类对象。

练习18.30

class Base {  protected:      int ival;  public:      Base() :ival(0) {};      Base(const Base &b) {        ival = b.ival;    }      Base(int a) :ival(a) {}  };  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:      Final() {}      Final(const Final &f) : Base(f), MI(f) {}      Final(int i) : Base(i) {}  }; 

第19章 特殊工具与技术

Date: 2017-10-10
练习19.1

#include <iostream>#include <cstdlib>void *operator new(size_t size) {    if (void *mem = malloc(size)        return mem;    else         throw bad_alloc();}void operator delete(void *mem) noexcept {    free(mem);}

练习19.2
自行尝试

练习19.3
(b)失败,原因是pb指向的对象不包含C对象。
(c)错误,原因是Ambiguous conversion from derived class ‘D’ to base class ‘A’. 如果将A的后续继承变成虚继承则可以转化。

练习19.4

A *pa = new C;try {    C &c = dynamic_cast<C&>(*pa);    // do something} catch(std::bad_cast e) {    cerr << e.what() << std::endl;}

练习19.5
并非所有情况都可以使用虚函数。当一个指向基类的指针,需要调用派生类自己定义的成员时,需要使用dynamic_cast.

练习19.6
见下

练习19.7
见下

练习19.8
见下

//19.6    Query_base *pb1 = new AndQuery(Query("value1"), Query("value2"));    Query_base *pb2 = new OrQuery(Query("value1"), Query("value2"));    if (AndQuery *pa1 = dynamic_cast<AndQuery*>(pb1)) {        cout << "成功" << endl;    } else {        cout << "失败" << endl;    }    if (AndQuery *pa2 = dynamic_cast<AndQuery*>(pb2)) {        cout << "成功" << endl;    } else {        cout << "失败" << endl;    }    //19.7    try {        AndQuery &ra1 = dynamic_cast<AndQuery&>(*pb1);        cout << "成功" << endl;    }    catch (bad_cast e) {        cout << e.what() << endl;    }    try {        AndQuery &ra2 = dynamic_cast<AndQuery&>(*pb2);        cout << "成功" << endl;    }    catch (bad_cast e) {        cout << e.what() << endl;    }    //19.8    if (typeid(*pb1) == typeid(*pb2))        cout << "pd1与pd2指向的对象类型相同" << endl;    else        cout << "pd1与pd2的动态类型不相同" << endl;    if (typeid(*pb1) == typeid(AndQuery))        cout << "pd1的动态类型是AndQuery" << endl;    else        cout << "pd1的动态类型并非是AndQuery" << endl;    

练习19.9
自行尝试

练习19.10
(a) class A的指针
(b) class A的指针
(c) class A

练习19.11
普通数据指针:指向某个对象或变量的内存地址
数据成员指针:指向类的成员(不指向类的对象)

练习19.12

class Screen {public:    static const pos Screen::*p() {        return &Screen::cursor;    }};

练习19.13

static const std::string Sales_data::* data() {      return &Sales_data::bookNo;    }    

练习19.14