Primer C++笔记记录(第十八章)

Primer C++笔记记录(第十八章)

本书为Primer C++ 中文第五版

练习题笔记

在这里插入图片描述

(18.1)
void test1() {
    int n;
    while(cin >> n && n != 0) {
        range_error r("error");
        if(n == 1) throw r;
        exception *p = &r;
        if(n == 2) throw *p;
        throw p;
    }
}
a>
terminate called after throwing an instance of 'std::range_error'
  what():  error
b>
throw *p:
terminate called after throwing an instance of 'std::exception'
  what():  std::exception
throw p:
terminate called after throwing an instance of 'std::exception*'
(18.2)
vector对象v会在它的析构函数被销毁;
而动态分配的对象数组将无法被正确的销毁;
文件流对象in会被销毁;
(18.3)
第一种方式是使用智能指针;第二种方法是使用类管理,在类的析构函数中销毁对象。
class Array {
public:
    Array(int n): ptr(new int[n]) { }
    ~Array() { delete[] ptr;}
private:
    int *ptr;
};

void test2(int *b, int *e) {
    vector<int> v(b, e);
    //int *p = new int(v.size());
    //1:
    shared_ptr<int> p(new int[v.size()], [](int *p) {delete[] p;});
    //2:
    Array p1(v.size());
    ifstream in("ints");
}

在这里插入图片描述

(18.4)
void test4() {
    try {
        exception e;
        runtime_error r("runtime_error test");
        overflow_error o("overflow_error test");
        int n;
        cin >> n;
        if(n == 0) throw e;
        else if(n == 1) throw r;
        throw o;
    }catch(overflow_error eobj) {
        cerr << "overflow_error catch" << endl;
    }catch(const runtime_error &re) {
        cerr << "runtime_error catch" << endl;
    }catch(exception) {
        cerr << "exception catch" << endl;
    }
}
(18.5)
void test5() {
    try{
        range_error r("range_error test");
        exception *p = &r;
        int n;
        cin >> n;
        if(n == 0 ) throw r;
        else if(n == 1) throw *p;
        throw p;
    }catch(range_error &r) {
        cerr << r.what() << " catch range_error" << endl;
    }catch(exception) {
        cerr << "catch exception" << endl;
    }catch(exception*) {
        cerr << "catch exception*" << endl;
    }
}
(18.6)
class exceptionType { };
typedef int EXCPTYPE;
void test6() {
    exceptionType* et;
    EXCPTYPE i;
    range_error o("range_error");
    try {
        int n;cin >> n;
        if(n == 0) throw et;
        else if(n == 1) throw i;
        throw o;
    }catch(EXCPTYPE) {
        cerr << "EXCPTYPE catch" << endl;
    }catch(exceptionType*) {
        cerr << "exceptionType* catch" << endl;
    }catch(...) {
        cerr << "... catch" << endl;
    }
}

在这里插入图片描述

(18.7)
处理构造函数初始值异常的唯一方式是将构造函数写成函数try语句块。
template <typename T>
Blob<T>::Blob(std::initializer_list<T> il) try:
    data(std::make_shared<std::vector<T>>(il)) {
        //...
    }catch(const std::bad_alloc &e) { handle_out_of_memory(e); }

在这里插入图片描述

(18.8)
略了

在这里插入图片描述

在这里插入图片描述

(18.9)
(18.10)
class isbn_mismatch : public logic_error {
public:
    isbn_mismatch(const string &s, const string &lhs, const  string &rhs)
        : logic_error(s), left(lhs), right(rhs) { }
    string left, right;
};
class Sales_data {
friend class isbn_mismatch;
public:
    Sales_data(string s, double d): isBn(s), units_sold(d) { }
    Sales_data& operator+=(const Sales_data& sd) {
        if(isbn() != sd.isbn()) throw isbn_mismatch("wrong isbns", isbn(), sd.isbn());
        units_sold += sd.units_sold;
        return *this;
    }
    string isbn() const { return isBn; }
    double us() const { return units_sold; }
private:
    string isBn;
    double units_sold = 0.0;
};
ostream& operator<<(ostream& os, const Sales_data& sd) {
    os << sd.isbn() << " " << sd.us() << endl;
    return os;
}
void test9() {
    Sales_data sd0("TLBB", 12.5), sd1("TLBB", 11), sd2("SDYXZ", 10);
    sd0+=sd1;
    cout << sd0;
    try{
        sd0+=sd2;
    }catch(isbn_mismatch &s) {
        cerr << s.what() << '\t' << s.left << '\t'<< s.right << endl;
    }
    //test10
    sd1+=sd2;
}

(18.11)
因为what是虚函数,为了是其派生类可以更好的去定义。

在这里插入图片描述

(18.12)
略了
(18.13)
未命名的命名空间中定义的变量拥有静态生命周期,它们在第一次使用前创建,并且知道程序结束才销毁
未命名的命名空间仅在特定的文件内部有效,其作用范围不会横跨多个不同的文件
在文件中进行静态声明的做法已经被c++标准取消了,现在的做法应该是用未命名的命名空间
(18.14)
mathLib::MatrixLib::matrix mathLib::MatrixLib::operator* (const matrix&, const matrix&);

在这里插入图片描述

(18.15)
using 声明语句一次只引入命名空间的一个成员;
using 指示后面是关键字 namespace 以及命名空间的名字,一次引入命名空间的所有成员;
using 指示只推荐在命名空间本身的实现文件中使用。
(18.16)
使用 using 声明时,
位置1:
error: ‘ivar’ is already declared in this scope
 using Exercise::ivar;
位置2:
error: redeclaration of ‘double dvar’
     double dvar = 3.1416;
使用 using 指示时,
位置1:
error: reference to ‘ivar’ is ambiguous
     ++ivar;
位置2:
error: reference to ‘ivar’ is ambiguous
     ++ivar;
(18.17)
namespace Exercise {
    int ivar = 0;
    double dvar = 0;
    const int limit = 1000;
}
int ivar = 0;
/*
using Exercise::ivar;
using Exercise::dvar;
using Exercise::limit;
using namespace Exercise;
*/
void test17() {
/*
using Exercise::ivar;
using Exercise::dvar;
using Exercise::limit;
using namespace Exercise;
*/
    double dvar = 3.1416;
    int iobj = limit + 1;
    ++ivar;
    ++::ivar;
}

在这里插入图片描述

(18.18)
Swap 是一个由标准库定义的模板函数。
通过声明我们使用的是 std::swap 函数作用域中的所有 swap 用法, 使用时将在标准库中为它的实参类型查找匹配模板。
如果 mem1 是 string 类型,它将使用 string 类型的标准库函数。
如果 mem1 是 int 类型,它将使用 int 类型的标准库模板版本。
(18.19)
该函数将为该特定调用使用 swap 匹配的 std 版本, 但是也不一定是使用 swap 的标准库版本。

在这里插入图片描述

(18.20)
>改变之前
compute() 不匹配, 会error
compute(const void *) 可匹配,0 转换为 void* 类型
compute(int) 完美匹配, 不用类型转换
compute(double, double = 3.4) 可匹配,0 转换为 double 类型
compute(char*, char* = 0) 可匹配,0 转换为 char* 类型
>改变之后
compute() 不匹配
compute(const void *) 可匹配

在这里插入图片描述

(18.21)
a>CADVehicle 公有继承 CAD, 私有继承 Vehicle, 一般情况下不会出问题,但是 CADVehicle 不能隐式转换为 Vehicle.
b>错误,不能继承相同的基类.
c>没有问题.
(18.22)
class A; class B; class C;class X; class Y; class Z; class MI;

class A {
public:
A() { std::cout << "A\n"; }
};
class B: public A {
public:
B() { std::cout << "B\n"; }
};
class C: public B {
public:
C() { std::cout << "C\n"; }
};
class X {
public:
X() { std::cout << "X\n"; }
};
class Y {
public:
Y() { std::cout << "Y\n"; }
};
class Z: public X, public Y {
public:
Z() { std::cout << "Z\n"; }
};
class MI: public C, public Z {
public:
MI() { std::cout << "MI\n"; }
};

void test22() {
    MI m;
}

在这里插入图片描述

(18.23)
都可以
class A {
public:
A() { std::cout << "A\n"; }
};
class B: public A {
public:
B() { std::cout << "B\n"; }
};
class C: public B {
public:
C() { std::cout << "C\n"; }
};
class X {
public:
X() { std::cout << "X\n"; }
};
class D : public X, public C {
public:
    D() {std::cout << "D\n"; }
};
void test23() {
    D *pd = new D;
    X *px = pd;
    A *pa = pd;
    B *pb = pd;
    C *pc = pd;
}
(18.24)
ZooAnimal *pb = new Panda ("ying_yang");
pb->print();//正确
pb->cuddle();//错误
pb->highlight();//错误
delete pb;//正确
(18.25)
namespace t25{

class Base1 {
public:
    virtual void print() { std::cout << "Base1\n"; }
    virtual ~Base1() { std::cout << "~Base1\n"; }
};
class Base2 {
public:
    virtual void print() { std::cout << "Base2\n"; }
    virtual ~Base2() { std::cout << "~Base2\n"; }
};
class D1 : public Base1 {
public:
    virtual void print() { std::cout << "D1\n"; }
    virtual ~D1() { std::cout << "~D1\n"; }
};
class D2 : public Base2 {
public:
    virtual void print() { std::cout << "D2\n"; }
    virtual ~D2() { std::cout << "~D2\n"; }
};
class MI : public D1, public D2{
public:
    virtual void print() { std::cout << "MI\n"; }
    virtual ~MI() { std::cout << "~MI\n"; }
};

void test25() {
    Base1 *pb1 = new MI;
    Base2 *pb2 = new MI;
    D1 *pd1 = new MI;
    D2 *pd2 = new MI;
    std::cout << "---->>> a <<<----\n";
    pb1->print();
    std::cout << "---->>> b <<<----\n";
    pd1->print();
    std::cout << "---->>> c <<<----\n";
    pd2->print();
    std::cout << "---->>> d <<<----\n";
    delete pb2;
    std::cout << "---->>> e <<<----\n";
    delete pd1;
    std::cout << "---->>> f <<<----\n";
    delete pd2;
}
}

---->>> a <<<----
MI
---->>> b <<<----
MI
---->>> c <<<----
MI
---->>> d <<<----
~MI
~D2
~Base2
~D1
~Base1
---->>> e <<<----
~MI
~D2
~Base2
~D1
~Base1
---->>> f <<<----
~MI
~D2
~Base2
~D1
~Base1

在这里插入图片描述在这里插入图片描述

(18.26)
修改结构体 MI;
添加
void print(int x) { Base1::print(x); }
(18.27)
a>MI派生类的所有属性都是可见的,除了那些私有的。
b>是的,基类中任何重复且非私有的名字都可以通过添加作用域操作符在foo中访问。
c>dval = Base1::dval + Derived::dval;
d>Base2::fval = *dvec.cbegin();
e>Derived::sval[0] = Base1::cval;

在这里插入图片描述

(18.28)
除了 Derived2 类里面的 ival 以外, 其他类的访问都需要加限定符.
namespace t28 {
struct Base {
    void bar(int i) { std::cout <<"Base_bar\n"; }
protected:
    int ival = 0;
};
struct Derived1 : virtual public Base {
    void bar(char c) { std::cout << "Derived1_bar\n"; }
    void foo(char c) { std::cout << "Derived1_foo\n"; }
protected:
    char cval = 'a';
};
struct Derived2 : virtual public Base {
    void foo(int i) { std::cout << "Derived2_foo\n"; }
protected:
    int ival = 1;
    char cval = 'b';
};
class VMI : public Derived1, public Derived2 {
public:
    void show() {
        std::cout << "Base --> " << Base::ival << " <--\n"
                  << "Derived1 --> " << Derived1::cval << " <--\n"
                  << "Derived2 --> " << Derived2::cval << ival << " <--\n";
        int i = 0;
        char a = 'a';
        bar(i);
        Base::bar(i);
        bar(a);
        Derived1::foo(a);
        Derived2::foo(i);
    }
};
void test() {
    VMI vm;
    vm.show();
}
}

在这里插入图片描述

namespace t29{
class Class {
public:
    Class() { std::cout << "Class()\n"; }
};
class Base : public Class {
public:
    Base() { std::cout << "Base()\n"; }
    Base(const Base& base) {cout << "Base(const Base&)\n";}
    Base(int i) { std::cout << "Base(int)\n"; }
};
class D1 : virtual public Base {
public:
    D1() { std::cout << "D1()\n"; }
    D1(const D1& d): Base(d) {cout << "D1(const D1&)\n";}
    D1(int i): Base(i) { std::cout << "D1(int)\n"; }
};
class D2 : virtual public Base {
public:
    D2() { std::cout << "D2()\n"; }
    D2(const D2& d): Base(d) {cout << "D2(const D2&)\n";}
    D2(int i): Base(i) { std::cout << "D2(int)\n"; }
};
class MI : public D1, public D2 {
public:
    MI() { std::cout << "MI()\n"; }
    MI(const MI& m): D1(m), D2(m) {cout << "MI(const MI&)\n";}
    MI(int i): D1(i), D2(i) { std::cout << "MI(int)\n"; }
};
class Final : public MI, public Class {
public:
    Final() { std::cout << "Final()\n"; }
    Final(const Final& f): MI(f), Class(), Base(f) {cout << "Final(const Final&)\n";}
    Final(int i): MI(i), Class(), Base(i) { std::cout << "Final(int)\n"; }
};

void test() {
    std::cout << "--->>> a <<<---\n";
    Final f;
    std::cout << "--->>> c <<<---\n";
    Base *pb;
    Class *pc;
    MI *pmi;
    D2 *pd2;
    std::cout << "--->>> ca <<<---\n";
    //pb = new Class;
    std::cout << "--->>> cb <<<---\n";
    //pc = new Final;
    std::cout << "--->>> cc <<<---\n";
    //pmi = pb;
    std::cout << "--->>> cd <<<---\n";
    pd2 = pmi;
}

void test30() {
    Final f = Final(1);
    std::cout << "---- >>>> <<<< ----\n";
    Final f1 = f;
}
}
(18.29)
a>
构造顺序:
Class()
Base()
D1()
D2()
MI()
Class()
Final()
析构顺序: 与构造顺序相反
b>
一个 Base, 两个 Class.
c>
a error: invalid conversion from ‘t29::Class*’ to ‘t29::Base*; 不能把基类指针赋值到派生类
b error: ‘t29::Class’ is an ambiguous base of ‘t29::Final’; Final 类有包含两个 Class 类,歧义
c error: invalid conversion from ‘t29::Base*’ to ‘t29::MI*; 和 a 一样的错误
d right, 派生类指针可以给基类指针赋值
(18.30)
需要注意的一个问题就是虚基类(Base)的初始化是在最外层的派生类(Final)中进行的, 如果最外层的派生类没有明确的初始化虚基类,程序会自动调用虚基类的默认构造函数.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值