- ubuntu12.04环境下使用kvm ioctl接口实现最简单的虚拟机
- Ubuntu 通过无线网络安装Ubuntu Server启动系统后连接无线网络的方法
- 在Ubuntu上搭建网桥的方法
- ubuntu 虚拟机上网方式及相关配置详解
CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.
这篇CFSDN的博客文章详解C++异常处理(try catch throw)完全攻略由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.
程序运行时常会碰到一些异常情况,例如:
这些异常情况,如果不能发现并加以处理,很可能会导致程序崩溃.
所谓“处理”,可以是给出错误提示信息,然后让程序沿一条不会出错的路径继续执行;也可能是不得不结束程序,但在结束前做一些必要的工作,如将内存中的数据写入文件、关闭打开的文件、释放动态分配的内存空间等.
一发现异常情况就立即处理未必妥当,因为在一个函数执行过程中发生的异常,在有的情况下由该函数的调用者决定如何处理更加合适。尤其像库函数这类提供给程序员调用,用以完成与具体应用无关的通用功能的函数,执行过程中贸然对异常进行处理,未必符合调用它的程序的需要.
此外,将异常分散在各处进行处理不利于代码的维护,尤其是对于在不同地方发生的同一种异常,都要编写相同的处理代码也是一种不必要的重复和冗余。如果能在发生各种异常时让程序都执行到同一个地方,这个地方能够对异常进行集中处理,则程序就会更容易编写、维护.
鉴于上述原因,C++ 引入了异常处理机制。其基本思想是:函数 A 在执行过程中发现异常时可以不加处理,而只是“拋出一个异常”给 A 的调用者,假定为函数 B.
拋出异常而不加处理会导致函数 A 立即中止,在这种情况下,函数 B 可以选择捕获 A 拋出的异常进行处理,也可以选择置之不理。如果置之不理,这个异常就会被拋给 B 的调用者,以此类推.
如果一层层的函数都不处理异常,异常最终会被拋给最外层的 main 函数。main 函数应该处理异常。如果main函数也不处理异常,那么程序就会立即异常地中止.
C++ 通过 throw 语句和 try...catch 语句实现对异常的处理。throw 语句的语法如下:
throw 表达式,
该语句拋出一个异常。异常是一个表达式,其值的类型可以是基本类型,也可以是类.
try...catch 语句的语法如下: try { 语句组 } catch(异常类型) { 异常处理代码 } ... catch(异常类型) { 异常处理代码 } 。
catch 可以有多个,但至少要有一个.
不妨把 try 和其后{}中的内容称作“try块”,把 catch 和其后{}中的内容称作“catch块”.
try...catch 语句的执行过程是:
例如下面的程序:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
#include <iostream>
using
namespace
std;
int
main()
{
double
m ,n;
cin >> m >> n;
try
{
cout <<
"before dividing."
<< endl;
if
( n == 0)
throw
-1;
//抛出int类型异常
else
cout << m / n << endl;
cout <<
"after dividing."
<< endl;
}
catch
(
double
d) {
cout <<
"catch(double) "
<< d << endl;
}
catch
(
int
e) {
cout <<
"catch(int) "
<< e << endl;
}
cout <<
"finished"
<< endl;
return
0;
}
|
程序的运行结果如下: 9 6↙ before dividing. 1.5 after dividing. finished 。
说明当 n 不为 0 时,try 块中不会拋出异常。因此程序在 try 块正常执行完后,越过所有的 catch 块继续执行,catch 块一个也不会执行.
程序的运行结果也可能如下: 9 0↙ before dividing. catch\(int) -1 finished 。
当 n 为 0 时,try 块中会拋出一个整型异常。拋出异常后,try 块立即停止执行。该整型异常会被类型匹配的第一个 catch 块捕获,即进入catch(int e)块执行,该 catch 块执行完毕后,程序继续往后执行,直到正常结束.
如果拋出的异常没有被 catch 块捕获,例如,将catch(int e),改为catch(char e),当输入的 n 为 0 时,拋出的整型异常就没有 catch 块能捕获,这个异常也就得不到处理,那么程序就会立即中止,try...catch 后面的内容都不会被执行.
如果希望不论拋出哪种类型的异常都能捕获,可以编写如下 catch 块:
1
2
3
|
catch
(...) {
...
}
|
这样的 catch 块能够捕获任何还没有被捕获的异常。例如下面的程序:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
#include <iostream>
using
namespace
std;
int
main()
{
double
m, n;
cin >> m >> n;
try
{
cout <<
"before dividing."
<< endl;
if
(n == 0)
throw
- 1;
//抛出整型异常
else
if
(m == 0)
throw
- 1.0;
//拋出 double 型异常
else
cout << m / n << endl;
cout <<
"after dividing."
<< endl;
}
catch
(
double
d) {
cout <<
"catch (double)"
<< d << endl;
}
catch
(...) {
cout <<
"catch (...)"
<< endl;
}
cout <<
"finished"
<< endl;
return
0;
}
|
程序的运行结果如下: 9 0↙ before dividing. catch (...) finished 。
当 n 为 0 时,拋出的整型异常被catchy(...)捕获.
程序的运行结果也可能如下: 0 6↙ before dividing. catch (double) -1 finished 。
当 m 为 0 时,拋出一个 double 类型的异常。虽然catch (double)和catch(...)都能匹配该异常,但是catch(double)是第一个能匹配的 catch 块,因此会执行它,而不会执行catch(...)块.
由于catch(...)能匹配任何类型的异常,它后面的 catch 块实际上就不起作用,因此不要将它写在其他 catch 块前面.
如果一个函数在执行过程中拋出的异常在本函数内就被 catch 块捕获并处理,那么该异常就不会拋给这个函数的调用者(也称为“上一层的函数”);如果异常在本函数中没有被处理,则它就会被拋给上一层的函数。例如下面的程序:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
|
#include <iostream>
#include <string>
using
namespace
std;
class
CException
{
public
:
string msg;
CException(string s) : msg(s) {}
};
double
Devide(
double
x,
double
y)
{
if
(y == 0)
throw
CException(
"devided by zero"
);
cout <<
"in Devide"
<< endl;
return
x / y;
}
int
CountTax(
int
salary)
{
try
{
if
(salary < 0)
throw
- 1;
cout <<
"counting tax"
<< endl;
}
catch
(
int
) {
cout <<
"salary < 0"
<< endl;
}
cout <<
"tax counted"
<< endl;
return
salary * 0.15;
}
int
main()
{
double
f = 1.2;
try
{
CountTax(-1);
f = Devide(3, 0);
cout <<
"end of try block"
<< endl;
}
catch
(CException e) {
cout << e.msg << endl;
}
cout <<
"f = "
<< f << endl;
cout <<
"finished"
<< endl;
return
0;
}
|
程序的输出结果如下: salary < 0 tax counted devided by zero f=1.2 finished 。
CountTa 函数拋出异常后自行处理,这个异常就不会继续被拋给调用者,即 main 函数。因此在 main 函数的 try 块中,CountTax 之后的语句还能正常执行,即会执行f = Devide(3, 0);.
第 35 行,Devide 函数拋出了异常却不处理,该异常就会被拋给 Devide 函数的调用者,即 main 函数。拋出此异常后,Devide 函数立即结束,第 14 行不会被执行,函数也不会返回一个值,这从第 35 行 f 的值不会被修改可以看出.
Devide 函数中拋出的异常被 main 函数中类型匹配的 catch 块捕获。第 38 行中的 e 对象是用复制构造函数初始化的.
如果拋出的异常是派生类的对象,而 catch 块的异常类型是基类,那么这两者也能够匹配,因为派生类对象也是基类对象.
虽然函数也可以通过返回值或者传引用的参数通知调用者发生了异常,但采用这种方式的话,每次调用函数时都要判断是否发生了异常,这在函数被多处调用时比较麻烦。有了异常处理机制,可以将多处函数调用都写在一个 try 块中,任何一处调用发生异常都会被匹配的 catch 块捕获并处理,也就不需要每次调用后都判断是否发生了异常.
有时,虽然在函数中对异常进行了处理,但是还是希望能够通知调用者,以便让调用者知道发生了异常,从而可以作进一步的处理。在 catch 块中拋出异常可以满足这种需要。例如:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
#include <iostream>
#include <string>
using
namespace
std;
int
CountTax(
int
salary)
{
try
{
if
( salary < 0 )
throw
string(
"zero salary"
);
cout <<
"counting tax"
<< endl;
}
catch
(string s ) {
cout <<
"CountTax error : "
<< s << endl;
throw
;
//继续抛出捕获的异常
}
cout <<
"tax counted"
<< endl;
return
salary * 0.15;
}
int
main()
{
double
f = 1.2;
try
{
CountTax(-1);
cout <<
"end of try block"
<< endl;
}
catch
(string s) {
cout << s << endl;
}
cout <<
"finished"
<< endl;
return
0;
}
|
程序的输出结果如下: CountTax error:zero salary zero salary finished 。
第 14 行的throw;没有指明拋出什么样的异常,因此拋出的就是 catch 块捕获到的异常,即 string("zero salary")。这个异常会被 main 函数中的 catch 块捕获.
为了增强程序的可读性和可维护性,使程序员在使用一个函数时就能看出这个函数可能会拋出哪些异常,C++ 允许在函数声明和定义时,加上它所能拋出的异常的列表,具体写法如下:
1
|
void
func()
throw
(
int
,
double
, A, B, C);
|
或 。
1
|
void
func()
throw
(
int
,
double
, A, B, C){...}
|
上面的写法表明 func 可能拋出 int 型、double 型以及 A、B、C 三种类型的异常。异常声明列表可以在函数声明时写,也可以在函数定义时写。如果两处都写,则两处应一致.
如果异常声明列表如下编写: void func() throw (),
则说明 func 函数不会拋出任何异常.
一个函数如果不交待能拋出哪些类型的异常,就可以拋出任何类型的异常.
函数如果拋出了其异常声明列表中没有的异常,在编译时不会引发错误,但在运行时, Dev C++ 编译出来的程序会出错;用 Visual Studio 2010 编译出来的程序则不会出错,异常声明列表不起实际作用.
C++ 标准库中有一些类代表异常,这些类都是从 exception 类派生而来的。常用的几个异常类如图 1 所示.
图1:常用的异常类 。
bad_typeid、bad_cast、bad_alloc、ios_base::failure、out_of_range 都是 exception 类的派生类。C++ 程序在碰到某些异常时,即使程序中没有写 throw 语句,也会自动拋出上述异常类的对象。这些异常类还都有名为 what 的成员函数,返回字符串形式的异常描述信息。使用这些异常类需要包含头文件 stdexcept.
下面分别介绍以上几个异常类。本节程序的输出以 Visual Studio 2010为准,Dev C++ 编译的程序输出有所不同。 1) bad_typeid 使用 typeid 运算符时,如果其操作数是一个多态类的指针,而该指针的值为 NULL,则会拋出此异常。 2) bad_cast 在用 dynamic_cast 进行从多态基类对象(或引用)到派生类的引用的强制类型转换时,如果转换是不安全的,则会拋出此异常。程序示例如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
#include <iostream>
#include <stdexcept>
using
namespace
std;
class
Base
{
virtual
void
func() {}
};
class
Derived :
public
Base
{
public
:
void
Print() {}
};
void
PrintObj(Base & b)
{
try
{
Derived & rd =
dynamic_cast
<Derived &>(b);
//此转换若不安全,会拋出 bad_cast 异常
rd.Print();
}
catch
(bad_cast & e) {
cerr << e.what() << endl;
}
}
int
main()
{
Base b;
PrintObj(b);
return
0;
}
|
程序的输出结果如下: Bad dynamic_cast.
在 PrintObj 函数中,通过 dynamic_cast 检测 b 是否引用的是一个 Derived 对象,如果是,就调用其 Print 成员函数;如果不是,就拋出异常,不会调用 Derived::Print.
3) bad_alloc 在用 new 运算符进行动态内存分配时,如果没有足够的内存,则会引发此异常。程序示例如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
#include <iostream>
#include <stdexcept>
using
namespace
std;
int
main()
{
try
{
char
* p =
new
char
[0x7fffffff];
//无法分配这么多空间,会抛出异常
}
catch
(bad_alloc & e) {
cerr << e.what() << endl;
}
return
0;
}
|
程序的输出结果如下: bad allocation ios_base::failure 。
在默认状态下,输入输出流对象不会拋出此异常。如果用流对象的 exceptions 成员函数设置了一些标志位,则在出现打开文件出错、读到输入流的文件尾等情况时会拋出此异常。此处不再赘述。 4) out_of_range 用 vector 或 string 的 at 成员函数根据下标访问元素时,如果下标越界,则会拋出此异常。例如:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
#include <iostream>
#include <stdexcept>
#include <vector>
#include <string>
using
namespace
std;
int
main()
{
vector<
int
> v(10);
try
{
v.at(100) = 100;
//拋出 out_of_range 异常
}
catch
(out_of_range & e) {
cerr << e.what() << endl;
}
string s =
"hello"
;
try
{
char
c = s.at(100);
//拋出 out_of_range 异常
}
catch
(out_of_range & e) {
cerr << e.what() << endl;
}
return
0;
}
|
程序的输出结果如下: invalid vector <T> subscript invalid string position 。
如果将v.at(100)换成v[100],将s.at(100)换成s[100],程序就不会引发异常(但可能导致程序崩溃)。因为 at 成员函数会检测下标越界并拋出异常,而 operator[] 则不会。operator [] 相比 at 的好处就是不用判断下标是否越界,因此执行速度更快.
到此这篇关于详解C++异常处理(try catch throw)完全攻略的文章就介绍到这了,更多相关C++异常内容请搜索我以前的文章或继续浏览下面的相关文章希望大家以后多多支持我! 。
原文链接:http://c.biancheng.net/view/422.html 。
最后此篇关于详解C++异常处理(try catch throw)完全攻略的文章就讲到这里了,如果你想了解更多关于详解C++异常处理(try catch throw)完全攻略的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
#include using namespace std; class C{ private: int value; public: C(){ value = 0;
这个问题已经有答案了: What is the difference between char a[] = ?string?; and char *p = ?string?;? (8 个回答) 已关闭
关闭。此题需要details or clarity 。目前不接受答案。 想要改进这个问题吗?通过 editing this post 添加详细信息并澄清问题. 已关闭 7 年前。 此帖子已于 8 个月
除了调试之外,是否有任何针对 c、c++ 或 c# 的测试工具,其工作原理类似于将独立函数复制粘贴到某个文本框,然后在其他文本框中输入参数? 最佳答案 也许您会考虑单元测试。我推荐你谷歌测试和谷歌模拟
我想在第二台显示器中移动一个窗口 (HWND)。问题是我尝试了很多方法,例如将分辨率加倍或输入负值,但它永远无法将窗口放在我的第二台显示器上。 关于如何在 C/C++/c# 中执行此操作的任何线索 最
我正在寻找 C/C++/C## 中不同类型 DES 的现有实现。我的运行平台是Windows XP/Vista/7。 我正在尝试编写一个 C# 程序,它将使用 DES 算法进行加密和解密。我需要一些实
很难说出这里要问什么。这个问题模棱两可、含糊不清、不完整、过于宽泛或夸夸其谈,无法以目前的形式得到合理的回答。如需帮助澄清此问题以便重新打开,visit the help center . 关闭 1
有没有办法强制将另一个 窗口置于顶部? 不是应用程序的窗口,而是另一个已经在系统上运行的窗口。 (Windows, C/C++/C#) 最佳答案 SetWindowPos(that_window_ha
假设您可以在 C/C++ 或 Csharp 之间做出选择,并且您打算在 Windows 和 Linux 服务器上运行同一服务器的多个实例,那么构建套接字服务器应用程序的最明智选择是什么? 最佳答案 如
你们能告诉我它们之间的区别吗? 顺便问一下,有什么叫C++库或C库的吗? 最佳答案 C++ 标准库 和 C 标准库 是 C++ 和 C 标准定义的库,提供给 C++ 和 C 程序使用。那是那些词的共同
下面的测试代码,我将输出信息放在注释中。我使用的是 gcc 4.8.5 和 Centos 7.2。 #include #include class C { public:
很难说出这里问的是什么。这个问题是含糊的、模糊的、不完整的、过于宽泛的或修辞性的,无法以目前的形式得到合理的回答。如需帮助澄清此问题以便重新打开它,visit the help center 。 已关
我的客户将使用名为 annoucement 的结构/类与客户通信。我想我会用 C++ 编写服务器。会有很多不同的类继承annoucement。我的问题是通过网络将这些类发送给客户端 我想也许我应该使用
我在 C# 中有以下函数: public Matrix ConcatDescriptors(IList> descriptors) { int cols = descriptors[0].Co
我有一个项目要编写一个函数来对某些数据执行某些操作。我可以用 C/C++ 编写代码,但我不想与雇主共享该函数的代码。相反,我只想让他有权在他自己的代码中调用该函数。是否可以?我想到了这两种方法 - 在
我使用的是编写糟糕的第 3 方 (C/C++) Api。我从托管代码(C++/CLI)中使用它。有时会出现“访问冲突错误”。这使整个应用程序崩溃。我知道我无法处理这些错误[如果指针访问非法内存位置等,
关闭。这个问题不符合Stack Overflow guidelines .它目前不接受答案。 我们不允许提问寻求书籍、工具、软件库等的推荐。您可以编辑问题,以便用事实和引用来回答。 关闭 7 年前。
已关闭。此问题不符合Stack Overflow guidelines 。目前不接受答案。 要求我们推荐或查找工具、库或最喜欢的场外资源的问题对于 Stack Overflow 来说是偏离主题的,因为
我有一些 C 代码,将使用 P/Invoke 从 C# 调用。我正在尝试为这个 C 函数定义一个 C# 等效项。 SomeData* DoSomething(); struct SomeData {
这个问题已经有答案了: Why are these constructs using pre and post-increment undefined behavior? (14 个回答) 已关闭 6
我是一名优秀的程序员,十分优秀!