异常机制

异常概念

  • 异常处理就是处理程序中的错误
  • 种类很多,包括返回异常等等

异常优势

  • 函数返回值可以忽略,但异常不可忽略,没有被捕获就会程序终止
  • 整形返回值没有任何语义信息,而异常包含语义信息,有时类名即可体现
  • 整形返回值缺乏上下文信息,异常作为一个类,可以拥有自己的成员,成员可以传递足够的信息
  • 异常处理可以在调用跳级,这是一个代码编写问题;假设在有多个函数调用栈中出现了某个错误,使用整型返回码要求你在每一个级函数中都要进行处理,而使用异常处理的栈展开机制,只需要在一处进行处理就可以了,不需要每一级函数都处理

异常基本语法

  • 异常捕获不是相互依赖的,可以跨函数
  • 异常时必须处理,否则程序会宕掉
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;

//异常基本语法
int divide(int x, int y)
{
	if (y == 0)
	{
		throw y;//抛异常
	}
	return x / y;
}

void test01()
{
	//尝试捕获异常
	try
	{
		divide(10, 0);
	}
	catch (int e)
	{
		//异常时根据类型进行匹配
		cout << "除数为" << e << "!" << endl;
	}
}

void CallDivide(int x,int y)
{
	divide(x,y);
}
void test02()
{
	try {
		CallDivide(10, 0);
	}
	catch (int e)
	{
		cout << "test02除数为" << e << "!" << endl;
	}
}

int main(void)
{
	test01();
	test02();
	return 0;
}

栈解旋

  • 抛出异常,函数中的局部变量会被析构,就跟return一样

异常接口声明

  • 为了加强程序的可读性,可以在函数声明中列出所有可能抛出异常的所有类型
    例如

    void func()throw(A,B,C)
    

    这个函数func能够只抛出类型A,B,C及其子类型的异常

  • 如果函数声明中没有包含异常接口声明,则此函数可以抛出任何类型的异常,例如:void func()

  • 一个不抛任何类型异常的函数可声明为:void func()throw()

  • 如果一个函数抛出它的异常接口声明所不允许抛出的异常,unexcepted函数会被调用,该函数默认行为调用terminate函数中断程序

  • 代码示例

    #define _CRT_SECURE_NO_WARNINGS
    #include<iostream>
    using namespace std;
    
    void func() throw(int,float,char)
    {
    	throw "abc";
    }
    //不能抛出异常
    void func2()throw()
    {};
    
    int main(void)
    {
    	try
    	{
    		func();
    	}
    	catch (const char* str)
    	{
    		cout << str << endl;
    	}
    	catch (int e)
    	{
    		cout << "异常" << e << endl;
    	}
    	catch (...)//捕获所有异常
    	{
    		cout << "捕获所有异常!" << endl;
    	}
    	return 0;
    }
    

异常类型和异常变量的生命期

  • throw的异常是有类型的,可以是数字、字符串、类对象

  • throw的异常是有类型的,catch需要严格匹配异常类型

  • 注意异常对象的内存模型

  • 异常捕获对象时可以使用对象的方法属性等

  • 捕获的是对象时要有对象的拷贝构造并且重载等号运算符

  • 代码示例

    #define _CRT_SECURE_NO_WARNINGS
    #include<iostream>
    using namespace std;
    
    void func01()
    {
    	throw 1;
    }
    
    void func02()
    {
    	throw "exception";
    }
    
    class MyException
    {
    public:
    	MyException(const char* str)
    	{
    		error = new char[strlen(str) + 1];
    		strcpy(error, str);	
    	}
    	
    	MyException(const MyException& ex)
    	{
    		this->error = new char[strlen(ex.error) + 1];
    		strcpy(this->error, ex.error);
    	}
    	MyException& operator=(const MyException& ex)
    	{	
    			if (this->error != NULL)
    			{
    				delete[] this->error;
    				this->error = NULL;
    			}
    			this->error = new char[strlen(ex.error) + 1];
    			strcpy(this->error, ex.error);
    	
    	}
    
    	void what()
    	{
    		cout << this->error << endl;
    	}
    	~MyException()
    	{
    		if (this->error != NULL)
    		{
    			cout << "~Myexception()..." << endl;
    			delete[] this->error;		
    		}
    	}
    
    public:
    	char* error;
    
    };
    
    void func03()
    {
    	throw MyException("我刚写的异常");
    }
    
    void test01()
    {
    	try
    	{
    		func01();
    	}
    	catch (int e)
    	{
    		cout << "test01捕获异常" << e << endl;
    	}
    
    	try
    	{
    		func02();
    	}
    	catch (const char* str)
    	{
    		cout << "func02捕获异常" << str << endl;
    	}
    
    	try
    	{
    		func03();
    	}
    	catch (MyException e)
    	{
    		cout << "catch()..." << endl;
    		e.what();
    	}
    }
    
    
    int main(void)
    {
    	test01();
    	return 0;
    }
    

    异常对象生命周期

    • 普通元素异常catch处理完只有就析构

    • catch如果用引用去接不会有拷贝构造,异常处理完析构

    • catch指针时,要先new对象,否则没有内存指针无法赋值

    • 代码示例

      #define _CRT_SECURE_NO_WARNINGS
      #include<iostream>
      using namespace std;
      
      class MyException
      {
      public:
      	MyException()
      	{
      		cout << "构造函数!" << endl;
      	}
      	MyException(const MyException& ex)
      	{
      		cout << "拷贝构造!" << endl;
      	}	
      	~MyException()
      	{
      		cout << "析构函数!" << endl;
      	}
      };
      
      void func()
      {
      	//throw MyException();//创建匿名对象,调用构造函数
      	throw new MyException();//catch用指针时,要new,否则会提前被析构
      }
      
      void test01()
      {
      	//可以有普通类型元素  引用  指针三种
      	try
      	{
      		func();
      	}
      	/*catch (MyException e)
      	{
      		cout << "普通元素异常捕获!" << endl;
      	}*/
      	/*catch (MyException &e)
      	{
      		cout << "引用异常捕获!" << endl;
      	}*/
      	catch (MyException* e)
      	{
      		cout << "指针异常捕获!" << endl;
      		delete e;
      	}
      }
      
      int main(void)
      {
      	test01();
      	return 0;
      }
      

      C++标准异常类

      • 一般是自己的异常类去继承标准exception类,标准库有限,可以添加自己信息

      • 如何编写自己的异常类

        • 继承标准异常类,否则容易导致程序混乱,开发难以协调
        • 继承标准类时,应该重载父类的what函数和虚析构函数
        • 栈展开过程中,要复制异常类型,要根据在类中添加的成员考虑是否提供自己的复制构造函数
  • 代码示例

    #define _CRT_SECURE_NO_WARNINGS
    #include<iostream>
    #include<stdexcept>
    using namespace std;
    
    class Person
    {
    public:
    	Person()
    	{
    		this->mAge = 0;
    	}
    	void setAge(int age)
    	{
    		if (age < 0 || age>100)
    		{
    			throw out_of_range("年龄应该在0-100之间");
    		}
    		this->mAge = age;
    	}
    public:
    	int mAge;
    };
    
    void test01()
    {
    	Person p;
    	try {
    		p.setAge(1000);	
    	}
    	catch (exception e)
    	{
    		cout << e.what() << endl;
    	}
    }
    
    class MyOutRange:public exception
    {
    public:
    	MyOutRange(const char* error)
    	{
    		this->pError = new char[strlen(error) + 1];
    		strcpy(this->pError, error);
    	}
    	~MyOutRange()
    	{
    		if (this->pError != NULL)
    		{
    			delete[] this->pError;
    		}
        }
    	virtual char const* what() const
    	{
    		return pError;
    	};
    public:
    	char* pError;
    };
    
    void func02()
    {
    	throw MyOutRange("我自己的的out_of_range!");
    }
    
    void test02()
    {
    	try 
    	{
    		func02();
    	}
    	catch (exception &e)//抛出异常类型不同
    	{
    		cout << e.what() << endl;
    	}
    }
    
    int main(void)
    {
    	test01();
    	test02();
    	system("pause");
    	return 0;
    }
    

继承在异常中的应用

代码示例

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<stdexcept>
#include<string>
using namespace std;

//异常基类
class BaseMyException
{
public:
	virtual void what() = 0;
	virtual ~BaseMyException()
	{
	};
};

class TargetSpaceNullExpection :public BaseMyException
{
public:
	virtual void what()
	{
		cout << "子类目标空间为空!" << endl;
	}
};

class SourceSpaceNullExpection :public BaseMyException
{
public:
	virtual void what()
	{
		cout << "源空间为空!" << endl;
	}
	~SourceSpaceNullExpection() {};
};

void copy_str(char* taget, char* source)
{
	if (taget == NULL)
	{
		throw TargetSpaceNullExpection();
		//cout << "目标空间为空" << endl;
		return;
	}
	if (source == NULL)
	{
		throw SourceSpaceNullExpection();
		//cout << "源空间为空" << endl;
	}
	int len = strlen(source) + 1;
	while (*source!='\0')
	{
		*taget = *source;
		taget++;
		source++;
	}
}

int main(void)
{
	char* source = new char[]("abcefg");
	 //char* source = "abcdef";
	char buf[1024] = {0};

	try
	{
		copy_str(buf,NULL);
	}
	catch (BaseMyException& ex)
	{
		ex.what();
	}
	cout << buf << endl;
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Mr_Csyn

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值