C及C++异常及处理方法,包括继承、构造、析构异常处理

C语言错误处理方法

1、返回值(if … else语句判断错误)

//源文件src、目标文件dst的打开、读取和写入伪代码
int copy(const char* src,const char* dst)
{
	open(src);//打开源文件
		return -1;
	open(dst);//打开目标文件
		return -2;
	read(src,buf);//读取源文件
		return -3;
	write(dst,buf);//写入目标文件
		return -4;
}
//根据不同异常返回值,进行不同处理
void fun()
{
	int ret;
	ret = copy(src,dst);
	if(ret == -1)
	{
	}
	else if(ret == -2)
	{
	}
	else if(ret == -3)
	{
	}
	else if(ret == -4)
	{
	}
}

2、errno(linux 系统调用)

error
1. error说明

  1. 程序在运行过程中会产生各种各样的错误,我们可以给每种类型的错误分配一个唯一的编号,就像给班里的学生分配学号一样,在C语言中,我们将此称为错误代码。
  2. <errno.h>是C标准库中的一个检查错误的头文件,errno 用来保存最后的错误代码,它是一个宏,被展开后是一个 int 类型的数据(在单线程程序中可以认为是一个全局变量),并且当前程序拥有读取和写入的权限。
  3. 当linux C api函数发生异常时,一般会将errno变量(需include errno.h)赋一个整数值,不同的值表示不同的含义,可以通过查看该值推测出错的原因。

2. 错误代码:EDOM,ERANGE

说明
EDOM域错误(Domain error)。某些数学函数仅针对一定范围内的数值有意义,我们将这个范围称为域(Domain)。例如,sqrt() 函数仅能对非负数求平方根,如果我们给 sqrt() 传递一个负数,那么 sqrt() 就会将 errno 设置为 EDOM。
ERANGE范围错误(Range error)。一个变量可以表示的数值范围是有限的,数值过大或者过小都有溢出的风险。例如,pow() 用来求一个数的次方,它的结果极有可能超出浮点类型变量的表示范围;再如,strtod() 用来将字符串转换成浮点数,也有可能会超出范围。在这些情况下,errno 都会被设置为 ERANGE。

3. errno的使用

  • 1、程序刚刚启动的时候,errno 被设置为 0;程序在运行过程中,任何一个函数发生错误都有可能修改 errno 的值,让其变为一个非零值,用以告知>用户发生了特定类型的错误。

标准库中的函数只会将 errno 设置为一个用以表明具体错误类型的非零值,不会再将 errno 设置回零值。

  • 2、再次设置 errno 的值会覆盖以前 errno 的值,如果想得知当前函数发生了什么错误,必须在函数调用结束后立即读取 errno 的值,因为后面的函数依然有可能出错,这样就会再次修改 errno 的值,将之前 errno 的值覆盖掉,我们就再也无法得知之前的函数发生了什么错误。
  • 3、在调用函数前最好将 errno 的值重新设置为 0。因为之前的函数很有可能已经设置了 errno 的值,如果我们没有将 errno 设置回 0,那么即使当前的函数没有出错,我们也会读取到一个非零值,而误以为是当前函数出错,这显然是不靠谱的。

错误代码仅仅是一个数字,并没有额外的结构,要想获取具体的错误信息,一般有两种方案:

使用 perror() 将错误信息(文本)打印到标准输出设备;
使用 strerror() 将错误代码转换成对应的文本信息。

void perror(const char *s);
perror函数声明在<stdio.h>中
依次输出参数字符串s,冒号,空格,根据errno确定的错误信息和换行符。

char *strerror(int enum);
strerror在<string.h>中声明

#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <math.h>

int main()
{
  errno = 0;
  sqrt(-1);
  perror("开平方参数错误");

  puts(strerror(EDOM));
  puts(strerror(errno));

  return 0;
}

3、goto语句(函数内局部跳转)

在这里插入图片描述

4、setjmp、longjmp

  1. 通常情况下,函数在执行完后会返回到调用它的地方继续执行,如果想要由函数直接跳转到另外一个函数, 则可以使用<setjmp.h>中提供的非局部跳转函数。
  2. <setjmp.h>中主要的是setjmp和longjmp两个函数,setjmp宏标记程序中的一个位置,随后又longjmp跳转到这个位置。这一机制主要用以错误处理。这两个函数的原型为:

int setjmp(jmpbuf env);
setjmp宏调用的参数为jmp_buff类型,同样在**<setjmp.h>中定义。setjmp将当前的环境(包括指向当前绘制的指针)保存到变量中以便在longjmp中调用,返回0.
void longjmp(jmp_buf env, int val);
使用
longjmp宏回到setjmp宏标记的地方,调用的参数是与setjmp同一个jmp_buf变量,longjmp首先根据jmp_buf恢复环境,然后从setjmp中返回,返回的值就是val的值(如果val为0,则setjmp返回1)。val实际上是发生跳转的位置的标志,可以根据该值判断当前代码的执行是从哪个longjmp**跳转过来的。

#include <setjmp.h>
#include <stdio.h>

static jmp_buf env;

void f1(void);
void f2(void);

int main()
{
  int ret;
  ret = setjmp(env);
  printf("setjmp returned %d\n", ret);
  if (ret != 0)
  {
    printf("Program terminates: longjmp called\n");
    return 0;
  }
  f1();
  printf("Program terminates normally\n");
  return 0;
}

void f1(void)
{
  printf("f1 begins\n");
  f2();
  printf("f2 returns");
}

void f2(void)
{
  printf("f2 begins\n");
  longjmp(env, 1);
  printf("f2 returns\n");
}

在这里插入图片描述

5、assert.h

断言函数声明在 <assert.h>
void assert(int expression);
assert函数是以宏的形式实现的。参数expression是一个在正常情况下应该为真的表达式。在执行assert函数时,会检测expression的值,如果结果为0(为假),则assert函数会向标准错误流输出断言失败的信息(包括断言函数的参数、调用断言函数的文件名和断言函数所在行号等信息),并调用abort函数终止程序。

#include <stdio.h>
#include <assert.h>

int main()
{
  int x;
  printf("请输入一个正整数:");
  scanf("%d", &x);

  assert(x > 0);

  printf("输入的数符合要求!\n");

  return 0;
}

C++ 异常

程序错误、异常(语法、抛出、捕获、传播)、栈展开

程序错误

编译错误,即语法错误。程序就无法被生成运行代码。
运行时错误
1、不可预料的逻辑错误
2、可以预料的运行异常
例如:

1、动态分配空间时可能不会成功
2、打开文件可能会失败
3、除法运算时分母可能为0
4、整数相乘可能溢出
5、数组越界……

1、异常语法

throw 表达式;

try
{
    //try语句块
}

catch(类型1  参数1{
    //针对类型1的异常处理
}

catch (类型2  参数2{
    //针对类型2的异常处理
}

catch (类型n  参数n)
{
    //针对类型n的异常处理
}

2、异常抛出

1、可以抛出内置类型异常,也可以抛出自定义类型异常
2、throw抛出一个类对象会调用拷贝构造函数
3、异常发生之前创建的局部对象被销毁,这一过程称为栈展开

3、异常捕获

1、一个异常处理器一般只捕捉一种类型的异常
2、异常处理器的参数类型和抛出异常的类型相同

…表示可以捕获任何异常

#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;
    }
    ~MyException()
    {
        cout << "~MyException" << endl;
    }

    const char *what() const
    {
        return message_.c_str();
    }
private:
    string message_;
};
double Divide(double a, double b)
{
    if (b == 0.0)
    {
        MyException e("division by zero");
        throw e;
    }
    else
        return a / b;
}
int main(void)
{
    try
    {
        cout << Divide(5.0, 0.0) << endl;
    }
    catch (MyException &e)
    {
        cout << e.what() << endl;
    }
}

在这里插入图片描述
程序自定义一个异常类型MyException,从输出可以看出,Divide函数内先构造一个MyException对象e,调用构造函数,因为e是局部对象需要被析构(栈展开),在析构前先调用拷贝构造函数构造另一个对象,这个对象将被catch 引用,最后这个对象在catch末尾也将被析构。

//假设throw 1,而没有对应的catch (int) ,即使存在catch (double) 也捕获不到,不会做类型转换,
#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;
	}
	~MyException()
	{
		cout << "~MyException" << endl;
	}

	const char *what() const
	{
		return message_.c_str();
	}
private:
	string message_;
};
double Divide(double a, double b)
{
	if (b == 0.0)
	{
		throw 1;		//throw一个整型变量
	}
	else
		return a / b;
}
int main(void)
{
	try
	{
		cout << Divide(5.0, 0.0) << endl;
	}
	catch (MyException &e)// ERROR,throw 1,这里自定义类型e没有办法捕获整型变量
	{
		cout << e.what() << endl;
	}
	
}

//解决方法1:catch (int)

	try
	{
		cout << Divide(5.0, 0.0) << endl;
	}
	catch (MyException &e)
	{
		cout << e.what() << endl;
	}
	catch (int)
	{
	  cout<<"int exception ..."<<endl;
	}

//解决方法2:此时会由catch (…) 捕获到,…表示可以捕获任何异常。

try
	{
		cout << Divide(5.0, 0.0) << endl;
	}
	catch (MyException &e)
	{
		cout << e.what() << endl;
	}
	/*catch (int)
	{
	  cout<<"int exception ..."<<endl;
	}*/
	catch (...)
	{
		cout << "catch a exception ..." << endl;
	}

假设没有构造局部对象,直接throw , 如 throw MyException(“division by zero”); 那么将不会调用拷贝构造函数,只存在一个对象,在catch的末尾被析构。

#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;
	}
	~MyException()
	{
		cout << "~MyException" << endl;
	}

	const char *what() const
	{
		return message_.c_str();
	}
private:
	string message_;
};
double Divide(double a, double b)
{
	if (b == 0.0)
	{
		throw MyException("division by zero");
	}
	else
		return a / b;
}
int main(void)
{
	try
	{
		cout << Divide(5.0, 0.0) << endl;
	}
	catch (MyException &e)
	{
		cout << e.what() << endl;
	}
	catch (int)
	{
	  cout<<"int exception ..."<<endl;
	}
}

在这里插入图片描述

4、异常传播

1、try块可以嵌套
2、程序按顺序寻找匹配的异常处理器,抛出的异常将被第一个类型符合的异常处理器捕获
如果内层try块后面没有找到合适的异常处理器,该异常向外传播,到外层try块后面的catch块中寻找
3、没有被捕获的异常将调用terminate函数,terminate函数默认调用abort终止程序的执行
4、可以使用set_terminate函数指定terminate函数在调用abort之前将调用的函数

//如果内层try块后面没有找到合适的异常处理器,该异常向外传播,到外层try块后面的catch块中寻找
#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;
	}
	~MyException()
	{
		cout << "~MyException" << endl;
	}

	const char *what() const
	{
		return message_.c_str();
	}
private:
	string message_;
};

int main(void)
{
	try
	{
		try
		{
			throw MyException("test exception");
		}
		catch (int)
		{
			cout << "Inner..." << endl;
			cout << "catch a int exception" << endl;
		}
	}
	catch (MyException &e)
    {
        cout << "Outer ..." << endl;
        cout << e.what() << endl;
    }
	return 0;
}

在这里插入图片描述

//如果内部可以捕获,同时又想要继续传播到外面,则需要重新抛出异常
try
    {
        try
        {
            throw MyException("test exception");
        }
        catch (int)
        {
            cout << "Inner ..." << endl;
            cout << "catch a int exception" << endl;
        }
        catch (MyException& e)
        {
          cout<<"Inner ..."<<endl;
          cout<<e.what()<<endl;
          throw e;
        }
    }
    catch (MyException &e)
    {
        cout << "Outer ..." << endl;
        cout << e.what() << endl;
    }

在这里插入图片描述

**没有被捕获的异常将调用terminate函数,terminate函数默认调用abort终止程序的执行**
#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;
	}
	~MyException()
	{
		cout << "~MyException" << endl;
	}

	const char *what() const
	{
		return message_.c_str();
	}
private:
	string message_;
};

int main(void)
{
	try
	{
		try
		{
			throw MyException("test exception");
		}
		catch (int)
		{
			cout << "Inner..." << endl;
			cout << "catch a int exception" << endl;
		}
	}
	catch (int)
	{
		cout << "Inner..." << endl;
		cout << "catch a int exception" << endl;
	}
	return 0;
}

在这里插入图片描述
上面这个对话框由abort()函数调出,可以直接在main()中加入abort(),即可看到终止对话框。

//可以使用set_terminate函数指定terminate函数在调用abort之前将调用的函数
#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;
	}
	~MyException()
	{
		cout << "~MyException" << endl;
	}

	const char *what() const
	{
		return message_.c_str();
	}
private:
	string message_;
};

void MyTerminate()
{
	cout << "MyTerminate ..." << endl;
}

int main(void)
{
	set_terminate(MyTerminate);
	try
	{
		try
		{
			throw MyException("test exception");
		}
		catch (int)
		{
			cout << "Inner..." << endl;
			cout << "catch a int exception" << endl;
		}
	}
	catch (int)
	{
		cout << "Outer ..." << endl;
		cout << "catch a int exception" << endl;
	}
	return 0;
}

在这里插入图片描述

5、栈展开

1、沿着嵌套调用链接向上查找,直至为异常找到一个catch子句。这个过程称之为栈展开。

  • 为局部对象调用析构函数
  • 析构函数应该从不抛出异常
    栈展开期间会执行析构函数,在执行析构函数的时候,已经引发的异常但还没处理,如果这个过程中析构函数又抛出新的异常,将会调用标准库的terminate函数。
  • 异常与构造函数
    构造函数中可以抛出异常。如果在构造函数函数中抛出异常,则可能该对象只是部分被构造。即使对象只是被部分构造,也要保证销毁已构造的成员。(如果成员是指针p,因为析构函数不会被调用,故不会执行一般的delete p; 很可能造成内存泄漏)
    1、为局部对象调用析构函数
//为局部对象调用析构函数
#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;
	}
	~MyException()
	{
		cout << "~MyException" << endl;
	}

	const char *what() const
	{
		return message_.c_str();
	}
private:
	string message_;
};

class Test
{
public:
	Test()
	{
		cout << "Test()...... "<< endl;
	}
	Test(const Test& other)
	{
		cout << "Copy Test()...... " << endl;
	}
	~Test()
	{
		cout << "~Test()...... " << endl;
	}
};

int main(void)
{
	try
	{
		Test t;
		throw MyException("test exception");
	}
	catch (MyException &e)
    {
        cout << "Outer ..." << endl;
        cout << e.what() << endl;
    }
	return 0;
}

在这里插入图片描述
2、析构函数应该从不抛出异常

#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;
	}
	~MyException()
	{
		cout << "~MyException" << endl;
	}

	const char *what() const
	{
		return message_.c_str();
	}
private:
	string message_;
};

class Test
{
public:
	Test()
	{
		cout << "Test()...... "<< endl;
	}
	Test(const Test& other)
	{
		cout << "Copy Test()...... " << endl;
	}
	~Test()
	{
		cout << "~Test()...... " << endl;
	}
};
class Obj
{
public:
	Obj()
	{
		cout << "Obj()...... " << endl;
	}
	Obj(const Obj& other)
	{
		cout << "Copy Obj()...... " << endl;
	}
	~Obj()
	{
		cout << "~Obj()...... " << endl;
	}
};
class Test2
{
public:
	Test2()
	{
		cout << "Test2()...... " << endl;
	}
	Test2(const Test2& other)
	{
		cout << "Copy Test2()...... " << endl;
	}
	~Test2()
	{
		cout << "~Test2()...... " << endl;
		throw 2;
	}
};

int main(void)
{
	try
	{
		Test2 t2;
		throw MyException("Test2 exception1");//抛出异常会调用Test2 的析构函数
	}
	catch (MyException &e)
    {
        cout << e.what() << endl;
    }
	catch (int)
	{
		cout <<"Catch a exception..." << endl;
	}
	return 0;
}

在这里插入图片描述
**throw MyException(“Test2 exception1”);**抛出异常会调用Test2 的析构函数,此时Test2 的析构函数还没处理完毕又在析构函数中抛出了新的异常

~Test2()
	{
		cout << "~Test2()...... " << endl;
		throw 2;
	}

这个异常还没有经过catch (int)的捕捉,程序就提前调用了terminate函数

3、构造函数中可以抛出异常。如果在构造函数函数中抛出异常,则可能该对象只是部分被构造。
即使对象只是被部分构造,也要保证销毁已构造的成员。

#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;
	}
	~MyException()
	{
		cout << "~MyException" << endl;
	}

	const char *what() const
	{
		return message_.c_str();
	}
private:
	string message_;
};
class Obj
{
public:
	Obj()
	{
		cout << "Obj()...... " << endl;
	}
	Obj(const Obj& other)
	{
		cout << "Copy Obj()...... " << endl;
	}
	~Obj()
	{
		cout << "~Obj()...... " << endl;
	}
};
class Test2
{
public:
	Test2()
	{
		cout << "Test2()...... " << endl;
		throw MyException("test2 exception");
	}
	Test2(const Test2& other)
	{
		cout << "Copy Test2()...... " << endl;
	}
	~Test2()
	{
		cout << "~Test2()...... " << endl;
	}
private:
	Obj obj_;
};

int main(void)
{
	try
	{
		Test2 t2;
	}
	catch (MyException &e)
    {
        cout << e.what() << endl;
    }
	return 0;
}

在这里插入图片描述
程序说明:
这里在Test2中定义了一个Obj对象成员,在定义t2 对象的时候,根据构造函数的调用顺序,先调用对象成员的构造函数,所以obj_构造成功以后,开始调用Test2的构造函数,此时抛出一个异常throw MyException(“test2 exception”);。但是obj_的析构函数被调用了,所以保证已构造的对象被销毁。
注意:
如果成员是指针p,因为析构函数不会被调用,故不会执行一般的delete p; 很可能造成内存泄漏

class Test2
{
public:
	Test2()
	{
		obj_ = new Obj;
		cout << "Test2()...... " << endl;
		throw MyException("test2 exception");
	}
	Test2(const Test2& other)
	{
		cout << "Copy Test2()...... " << endl;
	}
	~Test2()
	{
		delete obj_;
		cout << "~Test2()...... " << endl;
	}
private:
	Obj* obj_;//定义了对象指针
};

这里在Test2中定义了对象指针,此时**obj_**构造完成了,但是抛出异常,所以析构函数没有被调用,出现了内存泄漏。

6、 异常与继承

如果异常类型为C++的类,并且该类有其基类,则应该将派生类的错误处理程序放在前面,基类的错误处理程序放在后面

#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;
    }
    catch (MyExceptionD &e)
    {
        cout << "catch MyExceptionD ..." << endl;
        cout << e.what() << endl;
    }
    catch (MyException &e)
    {
        cout << "catch MyException ..." << endl;
        cout << e.what() << endl;
    }

    return 0;
}

在这里插入图片描述
更换基类和继承类的捕捉顺序

catch (MyException &e)
	{
		cout << "catch MyException ..." << endl;
		cout << e.what() << endl;
	}
	catch (MyExceptionD &e)
	{
		cout << "catch MyExceptionD ..." << endl;
		cout << e.what() << endl;
	}

在这里插入图片描述
图片解释:
派生类的异常能够被基类所捕获,且前面的异常处理程序能够匹配的话首先catch。
1、MyExceptionD e(“test exception”); 根据构造函数调用顺序,首先调用基类,然后是派生类
2、throw e; 根据拷贝构造函数调用顺序,首先调用基类,然后是派生类,因为是临时对象,所以接着调用析构函数。和构造函数顺序相反,先派生,后基类。
3、**catch (MyException& e)**基类捕捉到异常,然后析构一开始构造函数创建的对象,和构造函数顺序相反,先派生,后基类。

注意:
如果把基类的放在最前面,而且不是引用的形式,如 catch (MyException e); 那么将会被这个所catch 到,而且在构造e 的过程会有object slicing 的问题。举例如下:

catch (MyException e)//不是引用的形式
	{
		cout << "catch MyException ..." << endl;
		cout << e.what() << endl;
	}
	catch (MyExceptionD &e)
	{
		cout << "catch MyExceptionD ..." << endl;
		cout << e.what() << endl;
	}

在这里插入图片描述

7、异常与指针

抛出指针通常是一个坏主意,因为抛出指针要求在对应处理代码存在的任意地方都存在指针所指向的对象(注意此时throw抛出时复制的是指针本身,不会去复制指针指向的内容)

#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
	{
		throw new MyExceptionD("test exception");
	}
	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;
}

在这里插入图片描述
其中MyException, MyExeptionD类如上所示,现在MyExeptionD 对象是在堆上分配的,所以在catch 的时候还没释放,还可以访问到e->what(); 但需要自己在catch 末尾delete e;

假设将
throw new MyExceptionD("test exception");
换成
MyExceptionD e("test exception");
throw &e;

int main(void)
{
	try
	{
		MyExceptionD e("test exception");
		throw &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;
}

在这里插入图片描述
程序说明: 没有输出 test exception
即抛出局部对象的指针,由于在catch 时MyExeptionD 对象已经被析构了,所以访问不到e->what(); 即e是空悬指针。

还有一点是,任何类型的指针都能被void* 指针所捕获,如果将注释打开,那么由于排在前面,异常首先将被它所捕获。

int main(void)
{
	try
	{
		throw new MyExceptionD("test exception");
	}
	catch (void* e)
	{
	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;
}

在这里插入图片描述

8、异常规格说明

1

  1. 异常规格说明的目的是为了让函数使用者知道该函数可能抛出的异常有哪些。
    可以在函数的声明中列出这个函数可能抛掷的所有异常类型。
    例如:

void fun() throw(A,B,C,D);

  1. 若无异常接口声明,则此函数可以抛掷任何类型的异常。

3、

  1. 不抛掷任何类型异常的函数声明如下:

void fun() throw();

#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;
}

在这里插入图片描述
实际上编译会产生警告:

warning C4290: 忽略 C++ 异常规范,但指示函数不是 __declspec(nothrow)

就是说VC++编译器现在还不怎么支持异常规格说明.
举个例子说,
void fun(int n) throw (int, MyException, MyExceptionD);
没有声明double 类型的异常,但在函数内throw 1.0; 在外部catch (double) 还是会成功。

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值