zoukankan      html  css  js  c++  java
  • cppPrimer学习18th

    cppPrimer学习18th

    18.1

    在下列 throw 语句中异常对象的类型是什么?
    
    (a) range_error r("error");
    	throw r;
    (b) exception *p = &r;
    	throw *p;
    	
    a) range_error 的拷贝
    b) exception
    
    如果是throw p,那么抛出的是拷贝的指针,该内存在跳出时释放了,是有问题的
    

    18.2

    当在指定的位置发生了异常时将出现什么情况?
    
    void exercise(int *b, int *e)
    {
    	vector<int> v(b, e);
    	int *p = new int[v.size()];
    	ifstream in("ints");
    	//此处发生异常
    }
    
    p指向的内存得不到释放
    

    18.3

    要想让上面的代码在发生异常时能正常工作,有两种解决方案。请描述这两种方法并实现它们。
    
    
    void exercise(int *b, int *e)
    {
    	vector<int> v(b, e);
    	std::shared_ptr<int> p(new int[v.size()], [](int *p) { delete[] p; });
    	ifstream in("ints");
    	//此处发生异常
    }
    
    方法2,定义自己的类,实现自动的析构
    struct intArray
    {
        intArray() : p(nullptr) {}
        explicit    intArray(std::size_t s):
            p(new int[s])       {}
    
    
        ~intArray()
        {
            delete[] p;
        }
    
        //! data meber
        int *p;
    };
    
    使用 intArray p(v.size());
    

    18.4

    查看图18.1所示的继承体系,说明下面的 try 块有何错误并修改它。
    try {
    	// 使用 C++ 标准库
    } catch (exception) {
    	// ...
    } catch (const runtime_error &re) {
    	// ...
    } catch (overflow_error eobj) { /* ... */ }
    
    
    
    exception 基类应该放在最下面
    

    18.5

    // 修改下面的main函数,使其能捕获图18.1所示的任何异常类型:
    #include <exception>
    #include <iostream>
    #include <cstdlib>
    #include <stdexcept>
    #include <typeinfo>
    
    using namespace std;
    
    int main()
    {
    	try{
    		//使用C++标准库
    	}catch(bad_cast &r){
    		cout << r.what();
    		abort();
    	}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(runtime_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(logic_error &r){
    		cout << r.what();
    		abort();
    	}catch(bad_alloc &r){
    		cout << r.what();
    		abort();
    	}catch(exception &r){
    		cout << r.what();
    		abort();
    	}
    
    	return 0;
    }
    

    18.6

    已知下面的异常类型和 catch 语句,书写一个 throw 表达式使其创建的异常对象能被这些catch语句捕获:
    (a) class exceptionType { };
    	catch(exceptionType *pet) { }
    (b) catch(...) { }
    (c) typedef int EXCPTYPE;
    	catch(EXCPTYPE) { }
    
    
    a) exceptionType *pt=xxx;
        throw pt;
    b) 都可以
    c) throw (int)xx
    

    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.10

    程序将会执行terminate
    

    18.11

    一般我们都会打印 e.what, 如果watch异常,相当于 catch里面嵌套try{}catch
    

    18.13

    什么时候应该使用未命名的命名空间?
    达到static的效果,本文件可见 比如一些typedef等也可以
    

    18.14

    假设下面的operator*声明的是嵌套的命名空间 mathLib::MatrixLib 的一个成员:
    namespace mathLib {
    	namespace MatrixLib {
    		class matrix { /* ... */ };
    		matrix operator* (const matrix &, const matrix &);
    		// ...
    	}
    }
    请问你应该如何在全局作用域中声明该运算符?
    
    mathLib::MatrixLib::matrix  mathLib::MatrixLib::operator* (const matrix &, const matrix &);
    

    18.15

    说明 using 指示与 using 声明的区别。
    
    using指示 using namespace xxx; 可能有重名的与原来的命名空间
    using 声明 using std::cin;
    
    

    18.16

    18.17

    //假定在下面的代码中标记为“位置1”的地方是对命名空间 Exercise中所有成员的using声明,请解释代码的含义。
    // 如果这些using声明出现在“位置2”又会怎样呢?将using声明变为using指示,重新回答之前的问题。
    //  编译不过去
    
    #include <iostream>
    using namespace std;
    namespace Exercise
    {
        int ivar = 0;
        double dvar = 0;
        const int limit = 1000;
    } // namespace Exercise
    int ivar = 0;
    //位置1
    // using Exercise::dvar;
    // using Exercise::ivar; //1..编译不过去
    // using Exercise::limit;
    //  using namespace Exercise;
    
    int main(int argc, char const *argv[])
    {
        //位置2 编译不过去
        // using Exercise::dvar;
        // using Exercise::ivar;
        // using Exercise::limit;
        //using namespace Exercise;
    
        double dvar = 3.1416;
        int iobj = limit + 1;
        ++ivar;
        ++::ivar;
    
        std::cout << "ivar= " << ivar << std::endl;
        std::cout << "::ivar= " << ::ivar << std::endl;
        std::cout << "iobj= " << iobj << std::endl;
        while (1)
            ;
        return 0;
    }
    
    

    18.18

    已知有下面的 swap 的典型定义,当 mem1 是一个 string 时程序使用 swap 的哪个版本?
    如果 mem1 是 int 呢?说明在这两种情况下名字查找的过程。
    
    void swap(T v1, T v2)
    {
    	using std::swap;
    	swap(v1.mem1, v2.mem1);
    	//交换类型的其他成员
    }
    
    
    https://zh.cppreference.com/w/cpp/algorithm/swap
    1. mem1是string,应该有特例化的swap
    2. 为int 时 调用普通的模版函数
    

    18.19

    如果对swap的调用形如std::swap(v1.mem1, v2.mem1)将会发生什么情况?
    调用  std空间中的swap函数
    

    18.20

    在下面的代码中,确定哪个函数与compute调用匹配。
    列出所有候选函数和可行函数,对于每个可行函数的实参与形参的匹配过程来说,发生了哪种类型转换?
    
    namespace primerLib {
    	void compute();
    	void compute(const void *);
    }
    using primerLib::compute;
    void compute(int);
    void compute(double, double = 3.4);
    void compute(char*, char* = 0);
    
    void f()
    {
    	compute(0);
    }
    
    可能的匹配
    void compute(int);		// 最佳的匹配
    void compute(double, double = 3.4);		//int->double
    void compute(char*, char* = 0);			//int->char*
    primerLib::compute(const void *)		//int->const void *
        
       
    如果将using 放在main中的computer前,应该调用primerLib::compute(const void *)		//int->const void *
    

    18.21

    解释下列声明的含义,在它们当作存在错误吗?如果有,请指出来并说明错误的原因。
    (a) class CADVehicle : public CAD, Vehicle { ... };		公开继承CAD 私有继承Vehicle
    (b) class DbiList : public List, public List { ... };	非法duplicate base type 'List' invalid
    (c) class iostream : public istream, public ostream { ... };	合法公开继承
    

    18.22

    // 已知存在如下所示的类的继承体系,其中每个类都定义了一个默认构造函数:
    // class A { ... };
    // class B : public A { ... };
    // class C : public B { ... };
    // class X { ... };
    // class Y { ... };
    // class Z : public X, public Y { ... };
    // class MI : public C, public Z { ... };
    // 对于下面的定义来说,构造函数的执行顺序是怎样的?
    // MI mi;
    
    //A, B, C X, Y, Z, MI
    

    18.23

    使用练习18.22的继承体系以及下面定义的类 D,同时假定每个类都定义了默认构造函数,
    请问下面的哪些类型转换是不被允许的?
    
    class D : public X, public C { ... };
    D *pd = new D;
    (a) X *px = pd;
    (b) A *pa = pd;
    (c) B *pb = pd;
    (d) C *pc = pd;
    
    都允许,D都是他们的派生类
    

    18.24

    在第714页,我们使用一个指向 Panda 对象的 Bear 指针进行了一系列调用,
    假设我们使用的是一个指向 Panda 对象的 ZooAnimal 指针将会发生什么情况,请对这些调用语句逐一进行说明。
    

    ZooAnimal *p=new Panda();
    
    pe->print() 正确;		Panda::print
    pe->highlight() 错误;
    pe->toes() 错误;
    pe->duddle() 错误;
    delete pe 正确。		Panda::~Panda
    

    18.25

    假设我们有两个基类 Base1 和 Base2,它们各自定义了一个名为 print 的虚成员和一个虚析构函数。
    从这两个基类中文名派生出下面的类,它们都重新定义了 print 函数:
    
    class D1 : public Base1 { /* ... */};
    class D2 : public Base2 { /* ... */};
    class MI : public D1, public D2 { /* ... */};
    
    通过下面的指针,指出在每个调用中分别使用了哪个函数:
    
    Base1 *pb1 = new MI;
    Base2 *pb2 = new MI;
    D1 *pd1 = new MI;
    D2 *pd2 = new MI;
    
    (a) pb1->print();	MI	
    (b) pd1->print();	MI
    (c) pd2->print();	MI
    (d) delete pb2;		MI
    (e) delete pd1;		MI
    (f) delete pd2;		MI
    

    18.26

    已知如上所示的继承体系,下面对print的调用为什么是错误的?
    适当修改MI,令其对print的调用可以编译通过并正确执行。
    MI mi;
    mi.print(42);
    
    
    struct MI : public Derived, public Base2{
    
    void print(std::vector<double>){};
    void print(int x){
        Base1::print(x);
    }
    

    18.27

    已知如上所示的继承体系,同时假定为MI添加了一个名为foo的函数:
    int ival;
    double dval;
    void MI::foo(double cval)
    {
    	int dval;
    	//练习中的问题发生在此处
    }
    (a) 列出在MI::foo中可见的所有名字。
    
    Base1 : 	ival、dval、cval、print
    Base2 :		fval、print
    Derived:	sval、dval、print;
    MI			ival、dvec、print、foo
    
    
    
    (b) 是否存在某个可见的名字是继承自多个基类的?
    dval print ival
    
    
    (c) 将Base1的dval成员与Derived 的dval 成员求和后赋给dval的局部实例。
    (d) 将MI::dvec的最后一个元素的值赋给Base2::fval。
    (e) 将从Base1继承的cval赋给从Derived继承的sval的第一个字符。
    
    //加上 classname::var即可
    dval = Base1::dval + Derived::dval;
    Base2::fval = dvec.back();
    sval.at(0) = Base1::cval;
    

    18.28

    已知存在如下的继承体系,在 VMI 类的内部哪些继承而来的成员无须前缀限定符就能直接访问?
    哪些必须有限定符才能访问?说明你的原因。
    
    struct Base {
    	void bar(int);
    protected:
    	int ival;
    };
    struct Derived1 : virtual public Base {
    	void bar(char);
    	void foo(char);
    protected:
    	char cval;
    };
    struct Derived2 : virtual public Base {
    	void foo(int);
    protected:
    	int ival;
    	char cval;
    };
    class VMI : public Derived1, public Derived2 { };
    
    直接访问
    Derived1::bar  直接派生类有这个成员
    Derived2::ival
    需要的
    Base::bar 被派生类覆盖
    Base::ival
    Derived1::foo	派生类有同名的函数
    Derived2::foo
    
    

    18.29

    已知有如下所示的类继承关系:
    class Class { ... };
    class Base : public Class { ... };
    class D1 : virtual public Base { ... };
    class D2 : virtual public Base { ... };
    class MI : public D1, public D2 { ... };
    class Final : public MI, public Class { ... };
    
    (a) 当作用于一个Final对象时,构造函数和析构函数的执行次序分别是什么?
    
    Class Base D1 D2 Mi class Final
    
    (b) 在一个Final对象中有几个Base部分?几个Class部分?
    一个Base 两个class
    
    (c) 下面的哪些赋值运算符将造成编译错误?
    Base *pb; Class *pc; MI *pmi; D2 *pd2;
    (a) pb = new Class;		非法,使用派生类指针指向基类
    (b) pc = new Final;		非法,里面有两个class  
    						error: 'Class' is an ambiguous base of 'Final'
    						
    (c) pmi = pb;			派生=基类 非法
    (d) pd2 = pmi;			基类=派生 合法
    

    代码

    class Class
    {
    };
    class Base : public Class
    {
    };
    class D1 : virtual public Base
    {
    };
    class D2 : virtual public Base
    {
    };
    class MI : public D1, public D2
    {
    };
    class Final : public MI, public Class   //warning: direct base 'Class' inaccessible in 'Final' due to ambiguity 
                                            // 因为这里有两个class
    {
    
    };
    
    int main(int argc, const char **argv)
    {
        Base *pb;
        Class *pc;
        MI *pmi;
        D2 *pd2;
    
        pc = new Final;	//error: 'Class' is an ambiguous base of 'Final'
    
        return 0;
    }
    

    18.30

    // 18.30 在Base中定义一个默认构造函数、一个拷贝构造函数和一个接受int形参的构造函数。
    // 在每个派生类中分别定义这三种构造函数,每个构造函数应该使用它的形参初始化其Base部分。
    #include <iostream>
    
    using namespace std;
    
    class Class
    {
    public:
        Class() { cout << "class()" << endl; }
    };
    
    class Base : public Class
    {
    public:
        // Base() = default;
        Base() { cout << "Base()" << endl; }
        Base(int) { cout << "Base(int)" << endl; }
        Base(const Base &b) {}
    };
    
    class D1 : virtual public Base
    {
    public:
        D1() = default;
        D1(int i) : Base(i) { cout << "D1(int)" << endl; }
        D1(const D1 &d) {}
    };
    
    class D2 : virtual public Base
    {
    public:
        D2() = default;
        D2(int i) : Base(i) { cout << "D2(int)" << endl; }
        D2(const D2 &d) {}
    };
    
    class MI : public D1, public D2
    {
    public:
        MI() = default;
        MI(int i) : D1(i), D2(i) { cout << "MI(int)" << endl; }
        MI(const MI &m) {}
    };
    
    class Final : public MI, public Class
    {
    public:
        Final() = default;
        // Final(int i) : MI(i) { cout << "Final(int)" << endl; }
        Final(int i) : MI(i), Base(i) { cout << "Final(int)" << endl; }
        Final(const Final &f) {}
    };
    
    int main(int argc, char const *argv[])
    {
        Final f(1);
        // class()
        // Base(int)
        // D1(int)
        // D2(int)
        // MI(int)
        // class()
        // Final(int)
        while (1)
            ;
    
        return 0;
    }
    
  • 相关阅读:
    Conversions
    Mispelling4
    A hard puzzle
    Easier Done Than Said?
    利用map可以对很大的数出现的次数进行记数
    A+B Coming
    结构体成员变量
    NSString 类介绍及用法
    复习回顾
    函数与方法对比
  • 原文地址:https://www.cnblogs.com/zongzi10010/p/12822017.html
Copyright © 2011-2022 走看看