(P51)异常:异常与继承,异常与指针,异常规格说明,C++标准库异常层次

2024-06-08 05:58

本文主要是介绍(P51)异常:异常与继承,异常与指针,异常规格说明,C++标准库异常层次,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

    • 1.异常与继承
    • 2.异常与指针
    • 3.异常规格说明
    • 4.C++标准库异常层次

1.异常与继承

  • 如果异常类型为C++的类,并且该类有其基类(基类也是一个异常类),则应该将派生类的错误处理程序放在前面,基类的错误处理程序放在后面
  • eg:P51\01.cpp
#include <iostream>
#include <string>using  namespace std;class MyException
{
public:MyException( const  char *message): message_(message){cout <<  "MyException ..." << endl;}MyException( const MyException &other) : message_(other.message_){cout <<  "Copy MyException ..." << endl;}//因为是继承,最好声明为虚析构函数virtual ~MyException(){cout <<  "~MyException ..." << endl;}const  char *what()  const{return message_.c_str();}
private:string message_;
};class MyExceptionD :  public MyException
{
public:MyExceptionD( const  char *message): MyException(message){cout <<  "MyExceptionD ..." << endl;}MyExceptionD( const MyExceptionD &other): MyException(other){cout <<  "Copy MyExceptionD ..." << endl;}~MyExceptionD(){cout <<  "~MyExceptionD ..." << endl;}
};int main( void)
{try{MyExceptionD t;MyExceptionD e( "test exception");//抛出一个派生类异常,是可以被基类类型捕获的,因为派生类也是一种基类对象throw e;//先调用派生类构造函数,构造MyExceptionD对象}catch (MyExceptionD &e)//最好将派生类的异常处理程序放在前面,为了减少对象拷贝,最好声明为引用,这样就少一次对象的拷贝{                       //这里是引用是不会调用拷贝构造函数,相当于是一种参数传递,传递到catch语句块中输出异常对象的适合要进行栈展开,会寻找合适的catch语句块来处理,此时,局部对象t会调用析构函数进行自动销毁cout <<  "catch MyExceptionD ..." << endl;cout << e.what() << endl;}/*派生类的异常能够被基类所捕获,且前面的异常处理程序能够匹配的话首先catch,如果把基类的放在最前面,而且不是引用的形式,如 catch (MyException e); 那么将会被这个所catch 到,而且在构造e 的过程会有object slicing 的问题。*/catch (MyException &e)//基类的异常处理程序放在后面,如果这里不是引用传递,这里会被切割成基类对象,引用传递{                       //是不会构造一个新的对象出来的,所以最好将派生类的异常处理程序放在前面,以便类型完全匹配cout <<  "catch MyException ..." << endl;cout << e.what() << endl;}return  0;
}
  • 测试:
    在这里插入图片描述

2.异常与指针

  • 抛出指针通常是一个坏主意,因为抛出指针要求在对应处理代码存在的任意地方都存在指针所指向的对象(注意此时throw抛出时复制的是指针本身,不会去复制指针指向的内容)
    指针不能指向一个栈上的对象,因为栈上的对象会被销毁,它所指向的对象在指针存在的时候生命周期是不能结束的,否则指针则会出现空悬指针,指针所指向的对象已经被销毁了
  • eg:P51\02.cpp
#include <iostream>
#include <string>using  namespace std;class MyException
{
public:MyException( const  char *message): message_(message){cout <<  "MyException ..." << endl;}MyException( const MyException &other) : message_(other.message_){cout <<  "Copy MyException ..." << endl;}//因为是继承,最好声明为虚析构函数virtual ~MyException(){cout <<  "~MyException ..." << endl;}const  char *what()  const{return message_.c_str();}
private:string message_;
};class MyExceptionD :  public MyException
{
public:MyExceptionD( const  char *message): MyException(message){cout <<  "MyExceptionD ..." << endl;}MyExceptionD( const MyExceptionD &other): MyException(other){cout <<  "Copy MyExceptionD ..." << endl;}~MyExceptionD(){cout <<  "~MyExceptionD ..." << endl;}
};int main( void)
{try{/*即抛出局部对象的指针,由于在catch 时MyExeptionD 对象已经被析构了,所以访问不到e->what(); 即e是空悬指针。还有一点是,任何类型的指针都能被void* 指针所捕获,如果将catch (void* e);注释打开,那么由于排在前面,异常首先将被它所捕获。*/MyExceptionD e("test exception");throw &e;//抛出一个指针,此时抛出的是指针本身,不会去复制指针所指向的内容,通常不要//抛出指针,因为抛出指针还需要自己去释放指针对象,该对象不能是栈上的对象//如果是栈上的对象,那么在栈展开的时候这个局部对象会被销毁,会导致e指针是一个空悬指针,因为抛出一个指针//不是一个好主意}catch (void* e)//抛出的任何指针异常,都能被void*类型的指针所捕获{cout<<"catch void* ..."<<endl;cout<<((MyExceptionD*)e)->what()<<endl;delete (MyExceptionD*)e;}catch (MyExceptionD *e){cout <<  "catch MyExceptionD ..." << endl;cout << e->what() << endl;delete e;}catch (MyException &e){cout <<  "catch MyException ..." << endl;cout << e.what() << endl;}return  0;
}
  • 测试:
    在这里插入图片描述

  • eg:P51\03.cpp

#include <iostream>
#include <string>using  namespace std;class MyException
{
public:MyException( const  char *message): message_(message){cout <<  "MyException ..." << endl;}MyException( const MyException &other) : message_(other.message_){cout <<  "Copy MyException ..." << endl;}//因为是继承,最好声明为虚析构函数virtual ~MyException(){cout <<  "~MyException ..." << endl;}const  char *what()  const{return message_.c_str();}
private:string message_;
};class MyExceptionD :  public MyException
{
public:MyExceptionD( const  char *message): MyException(message){cout <<  "MyExceptionD ..." << endl;}MyExceptionD( const MyExceptionD &other): MyException(other){cout <<  "Copy MyExceptionD ..." << endl;}~MyExceptionD(){cout <<  "~MyExceptionD ..." << endl;}
};int main( void)
{try{//MyExceptionD e("test exception");//throw &e;//抛出一个指针/*现在MyExeptionD 对象是在堆上分配的,所以在catch 的时候还没释放,还可以访问到e->what(); 但需要自己在catch 末尾delete e;*/throw  new MyExceptionD( "test exception");}/*catch (void* e)//抛出的任何指针,都能被void*类型的指针所捕获{cout<<"catch void* ..."<<endl;cout<<((MyExceptionD*)e)->what()<<endl;delete (MyExceptionD*)e;}*/catch (MyExceptionD *e){cout <<  "catch MyExceptionD ..." << endl;cout << e->what() << endl;delete e;}catch (MyException &e){cout <<  "catch MyException ..." << endl;cout << e.what() << endl;}return  0;
}
  • 测试:
    在这里插入图片描述

3.异常规格说明

  • (1)异常规格说明的目的是为了让函数使用者知道该函数可能抛出的异常有哪些。
  • (2)可以在函数的声明中列出这个函数可能抛掷的所有异常类型。
    例如:
void fun() throw(A,B,C,D);
  • (3)若无异常接口声明,则此函数可以抛掷任何类型的异常。
  • (4)不抛掷任何类型异常的函数声明如下:
void fun() throw();
  • eg:P51\04.cpp
#include <iostream>
#include <string>using  namespace std;class MyException
{
public:MyException( const  char *message): message_(message){cout <<  "MyException ..." << endl;}MyException( const MyException &other) : message_(other.message_){cout <<  "Copy MyException ..." << endl;}//因为是继承,最好声明为虚析构函数virtual ~MyException(){cout <<  "~MyException ..." << endl;}const  char *what()  const{return message_.c_str();}
private:string message_;
};class MyExceptionD :  public MyException
{
public:MyExceptionD( const  char *message): MyException(message){cout <<  "MyExceptionD ..." << endl;}MyExceptionD( const MyExceptionD &other): MyException(other){cout <<  "Copy MyExceptionD ..." << endl;}~MyExceptionD(){cout <<  "~MyExceptionD ..." << endl;}
};void fun( int n)  throw ( int, MyException, MyExceptionD)
{if (n ==  1){throw  1;}else  if (n ==  2){throw MyException( "test Exception");}else  if (n ==  3){throw MyExceptionD( "test ExceptionD");}}void fun2()  throw()
{}int main( void)
{try{fun( 2);}catch ( int n){cout <<  "catch int ..." << endl;cout <<  "n=" << n << endl;}catch (MyExceptionD &e){cout <<  "catch MyExceptionD ..." << endl;cout << e.what() << endl;}catch (MyException &e){cout <<  "catch MyException ..." << endl;cout << e.what() << endl;}return  0;
}
  • 测试:
    就是说VC++编译器现在还不怎么支持异常规格说明,举个例子说,void fun(int n) throw (int, MyException, MyExceptionD); 没有声明double 类型的异常,但在函数内throw 1.0; 在外部catch (double) 还是会成功。
    在这里插入图片描述

4.C++标准库异常层次

  • 比如dynamic_cast 执行错误会产生bad_cast 异常,new 分配内存错误会产生bad_alloc 异常,其实这些异常类都继承自exception类,但内部的实现都没有有效的代码,只是用来标识当前程序产生了哪种类型的异常而已。
    在这里插入图片描述

  • 参考:从零开始学C++之异常(三):异常与继承、异常与指针、异常规格说明

这篇关于(P51)异常:异常与继承,异常与指针,异常规格说明,C++标准库异常层次的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1041366

相关文章

关于MongoDB图片URL存储异常问题以及解决

《关于MongoDB图片URL存储异常问题以及解决》:本文主要介绍关于MongoDB图片URL存储异常问题以及解决方案,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐... 目录MongoDB图片URL存储异常问题项目场景问题描述原因分析解决方案预防措施js总结MongoDB图

一文详解Java异常处理你都了解哪些知识

《一文详解Java异常处理你都了解哪些知识》:本文主要介绍Java异常处理的相关资料,包括异常的分类、捕获和处理异常的语法、常见的异常类型以及自定义异常的实现,文中通过代码介绍的非常详细,需要的朋... 目录前言一、什么是异常二、异常的分类2.1 受检异常2.2 非受检异常三、异常处理的语法3.1 try-

C++如何通过Qt反射机制实现数据类序列化

《C++如何通过Qt反射机制实现数据类序列化》在C++工程中经常需要使用数据类,并对数据类进行存储、打印、调试等操作,所以本文就来聊聊C++如何通过Qt反射机制实现数据类序列化吧... 目录设计预期设计思路代码实现使用方法在 C++ 工程中经常需要使用数据类,并对数据类进行存储、打印、调试等操作。由于数据类

usb接口驱动异常问题常用解决方案

《usb接口驱动异常问题常用解决方案》当遇到USB接口驱动异常时,可以通过多种方法来解决,其中主要就包括重装USB控制器、禁用USB选择性暂停设置、更新或安装新的主板驱动等... usb接口驱动异常怎么办,USB接口驱动异常是常见问题,通常由驱动损坏、系统更新冲突、硬件故障或电源管理设置导致。以下是常用解决

Linux下如何使用C++获取硬件信息

《Linux下如何使用C++获取硬件信息》这篇文章主要为大家详细介绍了如何使用C++实现获取CPU,主板,磁盘,BIOS信息等硬件信息,文中的示例代码讲解详细,感兴趣的小伙伴可以了解下... 目录方法获取CPU信息:读取"/proc/cpuinfo"文件获取磁盘信息:读取"/proc/diskstats"文

C++使用printf语句实现进制转换的示例代码

《C++使用printf语句实现进制转换的示例代码》在C语言中,printf函数可以直接实现部分进制转换功能,通过格式说明符(formatspecifier)快速输出不同进制的数值,下面给大家分享C+... 目录一、printf 原生支持的进制转换1. 十进制、八进制、十六进制转换2. 显示进制前缀3. 指

go 指针接收者和值接收者的区别小结

《go指针接收者和值接收者的区别小结》在Go语言中,值接收者和指针接收者是方法定义中的两种接收者类型,本文主要介绍了go指针接收者和值接收者的区别小结,文中通过示例代码介绍的非常详细,需要的朋友们下... 目录go 指针接收者和值接收者的区别易错点辨析go 指针接收者和值接收者的区别指针接收者和值接收者的

C++中初始化二维数组的几种常见方法

《C++中初始化二维数组的几种常见方法》本文详细介绍了在C++中初始化二维数组的不同方式,包括静态初始化、循环、全部为零、部分初始化、std::array和std::vector,以及std::vec... 目录1. 静态初始化2. 使用循环初始化3. 全部初始化为零4. 部分初始化5. 使用 std::a

C++ vector的常见用法超详细讲解

《C++vector的常见用法超详细讲解》:本文主要介绍C++vector的常见用法,包括C++中vector容器的定义、初始化方法、访问元素、常用函数及其时间复杂度,通过代码介绍的非常详细,... 目录1、vector的定义2、vector常用初始化方法1、使编程用花括号直接赋值2、使用圆括号赋值3、ve

如何高效移除C++关联容器中的元素

《如何高效移除C++关联容器中的元素》关联容器和顺序容器有着很大不同,关联容器中的元素是按照关键字来保存和访问的,而顺序容器中的元素是按它们在容器中的位置来顺序保存和访问的,本文介绍了如何高效移除C+... 目录一、简介二、移除给定位置的元素三、移除与特定键值等价的元素四、移除满足特android定条件的元