备忘:VC++ 中的异常处理

当程序遇到一个异常或一个严重的错误时,通常意味着它不能继续正常运行并且需要停止执行。任何的设计都离不开对异常与错误的处理。如果设计者不主动规避程序异常,往往在程序发生异常时,会被系统终止而直接退出。这对使用者来说,是很不友好的。

如果主动处理异常,可以显式地提示错误地发生,也可以避免程序异常终止。更好的处理是,在设计时,预估一些错误,让用户避开这些错误以让程序顺利运行。对一些无法预知的异常,也需要我们通过VC++的异常处理机制,显式地告诉错误已发生,并保证程序的继续进行。

VC++提供了两种不同性质的异常处理:
    结构化异常SEH (Structured Exception Handling, ) 和 类型化的 C++异常。
    SEH 是VC++编译器特有的,因此如果你想要编写可移植的代码,就不应当使用SEH。他只能在 Windows系统中产生作用。如果只想编写 Windows 应用。那么,最好选择SEH来处理异常。SEH 比 类型化的C++异常 更方便,更灵活。

结构化异常SEH

__try,__except,__finally   是Windows系列操作系统平台上提供的SEH的处理模型。

__try,__except:表示 try {}语句中发生异常,将转到 except () {}模块执行。如果try{}没有异常。except模块被跳过。继续执行 except {} 之后的代码。

__try, __finally :  表示try {}语句无论有没异常发生,finally{}模块都会被执行。__try __finally 没有太多用法,不作详细说明。只要用对地方就行了。

    ___leave : 一般用在 try{} 里。表示退出 try 模块,并不执行 leave之后的语句。根当于函数中的return. 不过,这里只退出try{}.

__try,__except 简单用法示范

int _tmain(int argc, _TCHAR* argv[])
{
    int v1, v2;
    v1 = 1; v2 = 0;
    double x=0;

    __try
    {
        // if (v2==0) ___leave;
        // 加上___leave这一句。leave 之后的代码都不会执行 跳出 try 执行 try except 之后的代码。
        x = v1 / v2; // x = myFunc(v1, v2);
        cout << x << endl;
    }
    __except (1) // 试着改为 0 或 -1 看看效果
    {
        cout << "Error!" << endl;
    }
    system("pause");
    return 0;
}

__except 后的括号里,是一个表达式或一个数值。表达式返回的也应是一个数值。这个数值 为 1-3 表明 except的三种处理方试:

  • __except(-1)  异常终止,退出程序。很少用这个。除非人为想终止自己的程序。
  • __except(0) 跳出当前异常,继续由下一个异常 except 来处理。
  • __except(1) 接受异常。执行异常 {} 中的语句。以上二个值 -1, 0 都不会执行 except{ ... } 中的语句。

这三个值,在 Windows 中,有三个常量代表:

EXCEPTION_CONTINUE_EXECUTION (– 1)
        EXCEPTION_CONTINUE_SEARCH (0)
        EXCEPTION_EXECUTE_HANDLER (1)

我们也可以通过一个函数来返回三个值。让 except 被触发时,作更精准的处理。而这个函数一般会调用 Windows的 两个错误代码处理的API函数:
        GetExceptionCode() : 返回错误代码;
        GetExceptionInformation() : 取得错误信息结构。

嵌入异常处理与自定义处理函数示例:

int ErrorFunc(unsigned int code, struct _EXCEPTION_POINTERS *ep)
{
    puts("my Error infomathion.");

    if (code == EXCEPTION_ACCESS_VIOLATION) {

        puts("caught AV as expected.");

        return EXCEPTION_EXECUTE_HANDLER; // =1 执行被调用的 except 处理过程

    }

    else {

        puts("didn‘t catch AV, unexpected.");

        return EXCEPTION_CONTINUE_SEARCH; // =0 继续让下一个 except处理

    };
}

double myFunc(int a, int b)
{
    double r;

    //exit(0);
    __try
    {
    r = a / b;
    cout << "Cacle..." << endl;
    }
    __except (ErrorFunc(GetExceptionCode(), GetExceptionInformation()))
    {
        // 如果 ErrorFunc 返回值为 1 下面的语句才会执行
        unsigned int code = GetExceptionCode();
        cout << "ERROR!:" << code  << endl;
        //throw;
    }
    cout << "...end1..." << endl;
    return r;
}

int _tmain(int argc, _TCHAR* argv[])
{
    int v1, v2;
    v1 = 1; v2 = 0;
    double x=0;

    __try
    {
        x = myFunc(v1, v2); // 调用函数,嵌入式 __try __except 示范。
        cout << x << endl;
    }
    __except (1)
    {
        cout << "Error!" << endl;
    }
    cout << "...end2..." << endl;
    system("pause");
    return 0;
}

SEH异常处理模型中,异常通过RaiseException()函数抛出。RaiseException()函数的作用类似于C++异常模型中的throw。可以通过这个函数,主动处理预知的异常。

主动抛出异常示例:

int _tmain(int argc, _TCHAR* argv[])
{
    int v1, v2;
    v1 = 1; v2 = 0;
    double x=0;

    __try
    {
        // x = v1 / v2;
        RaiseException(100, 0, 0, NULL); //主动抛出错误 100 是自己定义的异常代码
        cout << x << endl;
    }
    __except (1)
    {
        UINT code = GetExceptionCode();
        if (code==100) // 判断自己抛出的异常代码
          cout << "Error! Code:"<< code << endl;
    }
    cout << "...end..." << endl;
    system("pause");
    return 0;
}

类型化的 C++异常

C++的异常处理很简单,就是三个关键字 try, throw, catch来完成。他在很多地方有关系性作用。但这里只作简单地了解。

try
{
  //这里写入一些代码
  int a=10, b=0;
  int x;
  if (b==0)
    throw 1; // 抛出异常。终止以下的代码执行 。
  if (a==1);
    throw 2; // 再抛出个异常。终止以下的代码执行 。

  x = a/b; // b=0. 会产生运算异常。
}
catch int i
{
  if (i==1) // 由 throw 抛出的值 =1
    cout << "b=0 is error!" <<endl;
  if (i==2) // 由 throw 抛出的值 =2
    cout << "i=1 is error!" <<endl;
}
catch(...){} //接受所有异常

如 果想用 try{} catch{}来捕捉一些意想不到的异常,是靠不住的。也就是说他需要在 try {} 中主动发现异常,然后通过 throw, 抛出异常,让 catch来处理。如果是你发现不了的异常,如果无法throw出来,也就无法处理。如上例中: if (b==0) throw 1; 这句不要。执行到 x=a/b; 是会产生一个系统错误。try{}catch{} 中不会主动识别这个异常。但 try , catch 的用法有很多。可以找更多的资料去了解下。我也未了解清楚。

以下内容容易不被了解:

catch 没有捕获到匹配的异常的时候,会调用默认的终止函数。可以调用 set_terminate()来设置终止函数,参数是一个函数指针,类型是:void (*terminate)(); 实际上是调用默认的unexpected()函数,而这个默认的unexpected() 调用了 set_terminate() 中设定的终止函数。可以用set_unexpected()来设置unexpected。

如下:( 但这一段代码在VC下编译执行不会发生什么!)

void myUnexpected() {
    cerr << "unexpected called\n";
    throw 0;     // throws int (in exception-specification)
}

void myfunction() throw (int) {
    throw ‘x‘;   // throws char (not in exception-specification)
}
int _tmain(int argc, _TCHAR* argv[])
{
    int v1, v2;
    v1 = 1; v2 = 0;
    double x=0;
    set_unexpected(myUnexpected);
    try
    {
        myfunction();  // x = v1 / v2;
    }
    catch (int) {
        cerr << "caught int/n";
    }
    catch (...){
        cerr << "caught other exception (non-compliant compiler?)\n";
    };
}

VC 在Release方式下如果选择了编译器代码优化选项,则会去搜索try块中的代码, 如果没有找到throw代码, 就会认为try catch结构是多余的, 给优化掉。 这样造成在Release模式下。如果不在 try{}中主动throw抛出一个异常。在VC 的 Release编译模式下,try  catch 是被忽略掉的。

时间: 2024-10-15 09:05:21

备忘:VC++ 中的异常处理的相关文章

备忘之类中的static成员的访问方式

原文地址:http://leihuang.net/2014/05/19/List-Interviews/ 单链表的一些常见面试题汇总 单链表反转/逆序 求单链表倒数第N个数 找到单链表的中间结点 如何判断链表是否有环的存在 单链表建环,无环链表变有环 如何知道环的长度? 如何找出环的连接点在哪里? 删除单链表中的重复元素 下面我先简单叙述一下每道题的思路,然后把实现的程序一起贴出来,不会讲得太细,我觉得只要有了思路之后,接下来的难点就是语言上的一些细节问题了,这个不自己去实现,听别人讲是体会不到

备忘: Android中把EditText的输入内容转为大写

editText_SerialCode = (EditText) findViewById(R.id.editText_SerialCode); editText_SerialCode.addTextChangedListener(textWatcher); private TextWatcher textWatcher = new TextWatcher() { @Override public void onTextChanged(CharSequence s, int start, int

[备忘]WCF中使用MessageContract的一些注意点

准备使用WCF完成上传文件,以取代之前HTTP POST的方式. 但是调试了很久一直报错,后来经过一些修改终于通过,以下是一些可能需要注意的地方: 1.在WCF服务的OperatorContract 方法中,只能包含一个MessageContract参数(猜测) 2.包含MessageContract参数的这个方法,不能有string之类返回值,只能返回void(确定)

项目中oracle存储过程记录——常用语法备忘

项目中oracle存储过程记录--常用语法备忘 项目中需要写一个oracle存储过程,需求是收集一个复杂查询的内容(涉及到多张表),然后把符合条件的记录插入到目标表中.其中原表之一的日期字段是timestamp类型,目标表的字段是varchar2类型: 其中一些内容很常用,所以做下记录,供查找. 1.存储过程的格式 oracle存储过程和函数都可以实现,一般没有返回值,则采用存储过程,函数比sqlserver的功能强大.oracle变量定义最好加上前缀如V_,查询条件中变量名称和字段名称不能重复

备忘3:数据绑定中值的判断

昨天,在一个项目中,要用到Repeater循环获取值,然后根据某个字段的值进行判断,以决定是否需要添加某个样式.之前用的<%#Eval("Name").ToString()==name?"是":"否”%>,其中name为.cs页面一个公共string类型的变量,Name为数据库里表的一个nvarchar(50)类型的字段.通过调试发现数据源中的数据其实是和name的值相等的,但是判断的值一直是"否".后来,百度找到的方案是改

c# -- 读取文件夹中的所有文件(备忘)

读取选取的文件夾下的所有.txt文件 private void button1_Click(object sender, EventArgs e){ if (folderBrowserDialog1.ShowDialog() == DialogResult.OK) { string foldPath = folderBrowserDialog1.SelectedPath; MessageBox.Show("已选择文件夹:" + foldPath, "选择文件夹提示"

UML中关系的分类及其概念——总结备忘

UML中关系分类: 依赖:依赖是两个事物间的语义关系,其中一个事物(独立事物)发生变化会影响另一个事物(依赖事物)的语义. 关联:关联是类与类之间的联接,它使一个类知道另一类的属性和方法. 聚合:聚合是一种特殊类型的关联,它描述了整体和部分的结构关系. 组合:组合是关联的一种,是比集合关系强的关系. 泛化:泛化(继承)是一种特殊/一般关系,特殊元素(子元素)的对象可替代一般元素(父元素). UML中关系符号及对应的Java代表的含义 依赖关系         关联关系      聚合关系    

(练手备忘)汇编实现将输入的字符串中的空格去掉后反序输出

功能:任意输入一个字符串,去掉其中的空格后反序输出 注:使用 int 21h 里的 0AH 功能 输入一个字符串时,字符串的第一个字节存储的是字符串的最大长度,第二个字节存储的是实际读入字符的个数 编译器使用的是MASMPlus ;#Mode = DOS MAXLEN = 64 ;设置字符串的最大长度 SPACE = ' ' ;空格 datasg segment buffer db MAXLEN+1,0,MAXLEN+1 dup(0) ;字符串输入缓冲区 string db MAXLEN+3 d

shell中${##%%}代表啥,备忘

#!/bin/bash A=aigo.goto.aigo.goto echo ${A#*go} echo ${A##*go} echo ${A%.*} echo ${A%%go*} 执行结果 .goto.aigo.goto to aigo.goto.aigo ai 备忘如下: #*字串 --从左向右将变量A中最先出现的"字串"(最靠左)以及其左边的一切都去掉(因为有个*) ##*字串 --从左向右将变量A中最后出现的"字串"(最靠右)以及其左边的一切都去掉 %字串*