041-C++之异常处理
本帖最后由 moc 于 2018-9-13 22:12 编辑1、异常的简介
异常是程序在执行期间产生的问题,提供的一种转移程序控制权的机制,与函数机制独立和互补。
函数是一种以站结构展开的上下函数衔接的程序控制系统,异常是另一种控制结构,它依附于栈的结构,却可以同时设置多个异常作为网捕条件,以类型匹配在栈机制中跳跃回馈。
C++ 异常处理涉及到三个关键字:try、catch、throw。
异常处理的基本思想:
1. C++的异常处理机制使得异常的引发和异常处理不必在一个函数中,这样底层函数可以着重解决具体的问题,而不必过多的考虑异常的处理。上层调用者可以再适当的位置设计针对不同类型的异常处理。
2. 异常是专门针对抽象编程的一系列错误处理,C++中不能借助函数机制,因为栈结构的本质是先进后出,依次访问,无法跳跃,但错误处理机制遇到错误可以转到若干级之上进行错误处理。
3. 异常超脱于函数机制,决定了其对函数的跨越式回跳。即异常跨越函数。
2、异常的基本语法
throw表达式; // 抛出异常
//捕获并处理异常
try
{
// 保护代码
}catch( ExceptionName e1 )
{
// catch 块
}catch( ExceptionName e2 )
{
// catch 块
}catch( ... )//其他未知异常
{
// catch 块
}
1.若有异常则通过throw操作创建一个异常对象并抛掷;
2.将可能抛出的异常的程序嵌在try块中,try块内为保护代码。
3.catch子句在try之后,按顺序进行检查,匹配捕获并进行异常处理(或继续抛异常);
4.try语句没有发生异常,则跳过catch子句继续向下执行;
5.如果没有找到匹配的catch,即抛出的异常无人接受,激发最后一道防线,terminate函数(可修改),该函数引起运行终止和abort函数。
6. 类的构造函数无返回类型,不能通过返回值报告运行状态,可通过异常解决。
7. 异常机制与函数机制互不干涉,异常的捕捉方式是基于类型匹配,捕捉相当于函数返回类型匹配,而不是函数参数匹配,所以捕捉不用考虑一个抛掷中的多种数据类型匹配问题。
8. 异常匹配严格按照类型匹配。匹配苛刻程度与模板一样,不允许隐式类型转换。
void divide(int x, int y)
{
if (y == 0) // 0为除数
{
throw x; // 抛出 int类型异常
}
cout << "Divide结果:" << x / y << endl;
}
void myDivide0(int x, int y)
{
divide(x, y);
}
// 发生的异常必须在函数中处理,否者会产生函数中断
int main()
{
try
{
myDivide0(10, 2);
myDivide0(123, 0);
}
catch (int e) // 捕获int型异常
{
cout << e << "被零除" << endl;
}
catch (...) // 捕获漏网之鱼
{
cout << "其他未知的异常!" << endl;
cout << "我接受了异常,但我没有处理,我继续往外扔";
throw;
}
system("pause");
return 0;
}
3、栈解旋
异常抛出后,从进入try块起到异常被抛出前,这期间在栈上的构造的所有对象都会被析构,析构的顺序和构造的顺序相反,这一过程即栈解旋 。
函数抛出异常限定,可以在定义函数之后加throw说明。
class Test
{
public:
Test(int a = 0, int b = 0)
{
this->a = a;
this->b = b;
cout << "构造函数do\n";
}
~Test()
{
cout << "析构函数do\n";
}
private:
int a;
int b;
};
void myDivide()
{
Test t1(1, 2), tr2(3, 4);// 测试t1,t2的析构顺序
cout << "myDivide...发生异常\n";
throw Test();
}
void myDivide12() throw (int , char, char*)//该函数只能抛出int,char,char*类型的异常
{
throw Test();
}
void myDivide2() throw()// 该函数不会抛出任何异常
{
throw Test();// 非要抛出时有警告。
}
int main()
{
try
{
myDivide();
}
catch (int a)
{
cout << "int类型 异常\n";
}
catch (...)
{
cout << "未知 异常\n";
}
system("pause");
return 0;
}
4、异常变量的生命周期
throw “aaa”;是抛出的char*类型。
对于抛出类对象:
1.如果接受异常的时候使用一个异常变量,则执行copy构造函数构造异常变量;
2.使用引用的话会直接使用throw时候的那个对象
3.使用指针接的话,如果抛出时不new一个对象的话,会形成参野指针;
最终结论:类对象发生异常时,使用引用比较合适
class BadSrcType {};
class BadDestType {};
class BadProcessType
{
public:
BadProcessType()
{cout << "BadProcessType构造函数do\n";}
BadProcessType(BadProcessType &obj)
{cout << "BadProcessType拷贝函数do\n";}
~BadProcessType()
{cout << "BadProcessType析构函数do\n";}
};
// throw类对象 类型异常
void my_strcpy(char* to, char*from)
{
if (from == NULL)
{
throw BadSrcType();
}
if (to == NULL)
{
throw BadDestType();
}
// copy时 场景检查
if (*from == 'a')
{
cout << "开始BadProcessType类型异常\n ";
throw BadProcessType();
}
if (*from == 'b')
{
cout << "开始BadProcessType类型异常\n ";
throw &(BadProcessType());
}
if (*from == 'c')
{
cout << "开始BadProcessType类型异常\n ";
throw new BadProcessType;
}
while (*from != 0)
{
*to++ = *from++;
}
*to = '\0';
}
int main()
{
int ret = 0;
char buf1[] = "asdfghjkl";
char buf2 = { 0 };
try
{
my_strcpy(buf2, buf1);
}
catch (int e)
{
cout << e << "int类型异常\n";
}
catch (char* e)
{
cout << e << "char*类型异常\n";
}
catch (BadSrcType e)
{
cout << "BadSrcType类型异常\n";
}
catch (BadDestType e)
{
cout << "BadDestType类型异常\n";
}
//结论1:如果接受异常的时候使用一个异常变量,则执行copy构造函数构造异常变量
catch (BadProcessType e)
{
cout << "BadProcessType类型异常\n";
}
//结论2:使用引用的话会使用throw时候的那个对象
//catch (BadProcessType &e)
//{
// cout << "BadProcessType类型异常\n";
//}
//结论3:指针可以和引用写在一块,但引用/元素不能写在一块
catch (BadProcessType *e)
{
cout << "BadProcessType指针类型异常\n";
delete e;
}
catch (...)
{
cout << "出现未知异常!\n";
}
// 最终结论:类对象发生异常时,使用引用比较合适
system("pause");
return 0;
}
5、异常的层次结构
即继承在异常中的应用
对于同一种类型的异常;异常派生,按引用传递异常。
6、C++ 标准的异常
C++ 提供了一系列标准的异常,定义在 <exception> 中,我们可以在程序中使用这些标准的异常。它们是以父子类层次结构组织起来的,如下所示:
传送门
页:
[1]