第26课 - 异常处理 - 下
1. 问题一
有时在工程中关心是否产生了异常,而不关心具体的异常类型,C++语言中可以做到吗?
C++中的catch语句可以使用...捕获所有的异常。
#include <cstdlib>
#include <iostream>
using namespace std;
int test(int i)
{
if( i == 1 )
{
throw "p";
}
if( i == 2 )
{
throw 0.5;
}
if( i == 3 )
{
throw 3;
}
if( i == 4 )
{
throw 'c';
}
return i;
}
int main(int argc, char *argv[])
{
for(int i=0; i<10; i++)
{
try
{
cout<<test(i)<<endl;
}
catch(char e) //只能放在catch之前,否则成了死代码
{
cout<<"Exception: "<<e<<endl;
}
catch(...)
{
cout<<"Exception Occur"<<endl;
}
}
cout << "Press the enter key to continue ...";
cin.get();
return EXIT_SUCCESS;
}
运行结果:
0
Exceptin Occur
Exceptin Occur
Exceptin Occur
Exceptin:c
5
6
7
8
9
catch(...)可以捕获所有异常但却无法得到异常信息。
catch(...)一般作为最后一个异常处理块出现。
看见代码中的catch就要意识到这里在处理异常情况,而异常是在对应的try中产生的。
2. 问题二
在catch语句块中仍然可以抛出异常
#include <cstdlib>
#include <iostream>
using namespace std;
int test(int i)
{
if( (6 <= i) && (i <= 9) )
{
throw i;
}
return i;
}
int main(int argc, char *argv[])
{
try
{
for(int i=0; i<10; i++)
{
try
{
cout<<test(i)<<endl;
}
catch(int e)
{
cout<<"Exception: "<<e<<endl;
throw e; //有异常,终止了for循环
}
}
}
catch(int e)
{
cout<<"Catch: "<<e<<endl;
}
cout << "Press the enter key to continue ...";
cin.get();
return EXIT_SUCCESS;
}
运行结果:
0
1
2
3
4
5
Exception:6
Catch:6
3. 问题三
在catch(...)语句块中,可以铜鼓不带参数的throw语句抛出捕获的异常。
#include <cstdlib>
#include <iostream>
using namespace std;
int test(int i)
{
if( (6 <= i) && (i <= 9) )
{
throw i;
}
return i;
}
int main(int argc, char *argv[])
{
try
{
for(int i=0; i<10; i++)
{
try
{
cout<<test(i)<<endl;
}
catch(...)
{
cout<<"Exception Occur"<<endl;
throw;
}
}
}
catch(int e)
{
cout<<"Catch: "<<e<<endl;
}
cout << "Press the enter key to continue ...";
cin.get();
return EXIT_SUCCESS;
}
运行结果:
0
1
2
3
4
5
Exception Occur
Catch:6
不要在构造函数中抛出异常。在构造函数可能申请系统资源,而在构造函数中抛出异常会导致对象构造不完全。不完全对象的析构函数是不会被调用的,因此可能造成资源泄漏。
4. 问题4
不要在构造函数中抛出异常。在构造函数可能申请系统资源,而在构造函数中抛出异常会导致对象构造不完全。不完全对象的析构函数是不会被调用的,因此可能造成资源泄漏。语法上来说是合法的,但是会造成问题。
构造函数中的异常示例。
#include <cstdlib>
#include <iostream>
using namespace std;
class Test
{
int* p;
public:
Test()
{
cout<<"Test()"<<endl;
p = new int[5];
throw 10; /*构造函数里面抛出异常,在这里没有被处理,去上一层main函数中 */
}
~Test()
{
cout<<"~Test()"<<endl;
delete[] p;
}
};
int main(int argc, char *argv[])
{
try
{
Test t;
}
catch(int e) //在有异常离开 类的时候,没有调用析构函数,其中的内存就泄漏了
{
cout<<"Catch: "<<e<<endl;
}
cout << "Press the enter key to continue ...";
cin.get();
return EXIT_SUCCESS;
}
运行结果:
Test()
Catch:10
5. 工程中的异常应用
在工程中会定义一系列的异常类。
通过继承,可以得到一个异常类族。
每个类代表工程中可能出现的一种异常类型。
由于对象构造与拷贝的开销,在定义catch语句块是使用引用作为参数。
在工程中可以使用标准库中的异常类
可以将标准库中的异常类作为基类派生新的异常类。
标准库中的异常都是从exception类派生的。
exception类有两个主要的分支:logic_error用于描述程序中出现的逻辑错误,如—传递无效参数;runtime_error用于描述无法预料的事件所造成的错误,如—内存耗尽,硬件错误。
标准库中的异常
logic_error和runtime_error都提供了一个参数为字符串的构造函数,这样就可以保持错误的信息。
通过what()成员函数就可以得到错误的信息。
异常的工程应用初探
#include <cstdlib>
#include <iostream>
#include <stdexcept>
using namespace std;
class divide_by_zero : public logic_error
{
public:
divide_by_zero(const char* s) : logic_error(s)
{
}
};
double Div(double a, double b)
{
if( (-0.00000001 < b) && ( b < 0.00000001) )
{
throw divide_by_zero("Divide by zero...");
}
return a / b;
}
int main(int argc, char *argv[])
{
try
{
cout<<Div(1, 0)<<endl;
}
catch(exception& e)
{
cout<<e.what()<<endl;
}
cout << "Press the enter key to continue ...";
cin.get();
return EXIT_SUCCESS;
}
运行结果:
Divide by zero...
6. 函数级try语法
可以将函数体作为一个完整的try语句块。
int func(int i)
{
try
{
return i;
}
catch(...)
{
return -1;
}
}
等价于
int func(int i) try
{
return i;
catch(...)
{
return -1;
}
}
函数级try语法可以更好将正常逻辑代码与异常处代码分开,提高了代码的可读性与维护性。
#include <cstdlib>
#include <iostream>
#include <stdexcept>
using namespace std;
int func1(int i)
{
try
{
if( i > 0 )
{
return i;
}
else
{
throw "error";
}
}
catch(...)
{
return -1;
}
}
int func2(int i) try
{
if( i > 0 )
{
return i;
}
else
{
throw "error";
}
}
catch(...)
{
return -1;
}
int main(int argc, char *argv[])
{
for(int i=0; i<5; i++)
{
cout<<func2(i)<<endl;
}
cout << "Press the enter key to continue ...";
cin.get();
return EXIT_SUCCESS;
}
运行结果:
-1
1
2
3
4
小结:
catch(...)可以捕获所有异常。
catch(...)经常作为最后一个catch语句出现。
不要在构造函数中够抛出异常,这样可能造成资源泄露。
工程中经常以标准库中的异常类作为项目异常的基础。
函数级try语句块能够更好的提高代码的维护性。