《C++ Primer 4 Answer Book》 整理[16]——第17章 用于大型程序的工具

本博客详细解析了C++中用于大型程序的工具,包括异常对象类型的判断、异常抛出规则、try-catch块的正确使用、以及如何确保代码异常安全。同时,深入探讨了auto_ptr的合法声明及其可能引发的错误,最后分析了函数异常说明的意义以及如何避免潜在的编译错误。通过实例解析,读者能够掌握C++中异常处理的精髓。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


17   用于大型程序的工具

        12717.1下面的throw语句中,异常对象的类型是什么?

        a)range_errorr(“error”); throw r;

        b)exception*p=&r; throw *p;

        答:a)异常对象r的类型是range_error

                  b)被抛出的异常对象是对指针p解引用的结果,其类型与p的静态类型相匹配,为exception

 

        12817.2如果上题中第二个throw语句写成throwp,会发生什么情况?

        答:如果r是一个局部对象,则throw p抛出的p是指向局部对象的指针,那么,在执行对应异常处理代码时,有可能对象r已不存在,从而导致程序不能正确运行。所以,通常throw语句不应该抛出指针,尤其不应该抛出指向局部对象的指针。

 

        12917.3解释下面try块为什么不正确。

                  try{

                           

                  }catch(exception){

                           

                  }catch(construntime_error &re){

                           

                  }catch(overflow_erroreobj){…}

        答:该try块中使用的exceptionruntime_erroroverflow_error是标准库中定义的异常类,它们是因继承而相关的:runtime_error类继承exception类,overflow_error类继承runtime_error类,在使用来自继承层次的异常时,catch子句应该从最低派生类型到最高派生类型排序,以便派生类型的处理代码出现在基类类型的catch之前,所以,上述块中catch子句的顺序错误。

        

        13017.5对于下面的异常类型以及catch子句,编写一个throw表达式,该表达式创建一个可被每个catch子句捕获的异常对象。

        a)classexceptionType{};

          catch(exceprionType *pet){}

        b)catch(…){};

        c)enummathErr{overflow,underflow,zeroDivide};

          catch(mathErr &ref){}

        d)typedefint EXCPTYPE;

          catch(EXCPTYPE){}

        答:a)throw new exceptionType(); //

                  b)throw8; //

                 c)throwoverflow; //

                  d)throw10; //

        

        13117.6给定下面的函数,解释当发生异常时会发生什么?

                  voidexercise(int *b,int *e)

                  {

                           vector<int>v(b,e);

                           int*p=new int(v.size());

                           ifstreamin(“ints”);

                           //exceptionoccurs here

                  }

        答:在new操作之后发生的异常使得动态分配的数组没有被撤销。

 

        13217.7有两种方法可以使上面的代码是异常安全的,描述并实现它们。

        答:一种方法是将有可能发生异常的代码放在try块中,以便在异常发生时捕获异常:

                  voidexercise(int *b,int *e)

                  {

                           vector<int>v(b,e);

                           int*p=new int(v.size());

                           try{

                                    ifstreamin(“ints”);

                                    //exceptionoccurs here

                           }

                           catch{

                                    deletep;

                           }

                 }

                  另一种方法是定义一个类来封装数组的分配和释放,以保证正确释放资源:

                  classResource{

                  public:

                           Resource(size_tsz):r(new int[sz]){}

                           ~Resource(){if(r) delete r;}

                  private:

                           int*r;

                  };

                  函数exercise相应修改为:

                  voidexercise(int *b,int *e)

                  {

                           vector<int>v(b,e);

                           Resourceres(v.size())

                           ifstreamin(“ints”);

                           //exceptionoccurs here

                  }

        

        13317.8下面的auto_ptr声明中,哪些是不合法的或者可能导致随后的程序错误?解释每个声明的问题。

        intix=1024,*pi=&ix,*pi2=new int(2048);

        typedefauto_ptr<int> IntP;

        a)IntP p0(ix);     

                  不合法,必须向auto_ptr的构造函数传递一个由new操作返回的指针,ix不是指针;

        b)IntP p1(pi);

                  可能导致随后的程序错误:auto_ptr只能用于管理从new操作返回的一个对象,p1是指向ix的指针,而ix是静态分配的对象;

        c)IntP p2(pi2);

                  正确;

        d)IntP p3(&ix);

                  可能导致随后的程序错误:auto_ptr只能用于管理从new操作返回的一个对象,此处传给auto_ptr构造函数的是静态分配的对象ix的地址;

        e)IntP p4(new int(2048));

                  正确;

        f)IntP p5(p2.get());

                  可能导致随后的程序错误:因为两个auto_ptr对象p2p5拥有同一基础对象(保存相同的指针),会导致同一指针被delete两次。

 

        13417.10如果函数有形如throw()的异常说明,它能抛出什么异常?如果没有异常说明呢?

        答:如果函数有形如throw()的异常说明,则该函数不抛出任何异常

                  如果函数没有异常说明,则该函数可以抛出任意类型的异常

 

        13517.11如果有,下面哪个初始化是错误的,为什么?

                  voidexample() throw(string);

                  a)void(*pf1)()=example;

                  b)void(*pf2)() throw() example;

                  答:b)是错误的,因为用另一个指针初始化带有异常说明的函数指针时,源指针的异常说明必须至少与目标指针一样严格。函数指针pf2的声明指出,pf2指向不抛出任何异常的函数,而example函数的声明指出它能抛出string类型的异常,example函数抛出的异常类型超过了pf2所指定的,所以,对pf2而言,example函数不是有效的初始化式,会引发编译时错误。

        

        13617.12下面的函数可以抛出哪些异常?

        a)voidoperator[] throw(logic_error);

        b)intop(int) throw(underflow_error,overflow error);

        c)charmanip(string) throw();

        d)voidprocess();

        答:异常说明指定,如果函数抛出异常,被抛出的异常将是包含在该说明中的一种,或者是从列出的异常类型中派生的类型,因此:

        a)可以抛出logic_errordomain_error等等异常;

        b)可以抛出underflow_erroroverflow_error类型的异常;

        c)不抛出任何异常

        d)可以抛出任意类型的异常

        

        13717.18何时可以使用未命名的命名空间?

        答:通常,当需要声明局部于文件的实体时,可以使用未命名的命名空间(在文件的最外层作用域中定义未命名的命名空间。

        

        13817.25给定下面的类层次,其中每个类定义了一个默认构造函数:

        classX{…};

        classA{…};

        classB:public A{…};

        classC:private B{…};

        classD:public X,public C{…};

        如果有,下面转换中哪些是不允许的?

        D*pd =new D;

        a)X*px=pd;

        b)A*pa=pd;

        c)B*pb=pd;

        d)C*pc=pd;

        答:c)b)是不允许的。

        因为CB的继承是私有继承,使得在DB的默认构造函数成为不可访问的,所以尽管存在从”D””B”以及从”D””A”的转换,但这些转换不可访问。

        

        13917.34有一种情况下派生类不必为虚基类提供初始化式,这种情况是什么?

        答:派生类不必为虚基类提供初始化式的情况是:虚基类具有默认构造函数;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值