SEH与C++ 异常模型在混合使用时注意情况与错误C2712、C2713

2024-03-01 21:48

本文主要是介绍SEH与C++ 异常模型在混合使用时注意情况与错误C2712、C2713,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

SEH与C++ 异常模型在混合使用时注意情况与错误C2712、C2713

相关参考:

http://www.360doc.com/content/12/0405/15/1016783_201118203.shtml

https://blog.csdn.net/ShiQW5696/article/details/80664749

一、分割开的混合使用

#include "stdafx.h"
#include <iostream>
#include <Windows.h> 
using namespace std;class  A
{
public:void  f1() {}void  f2() { throw 888; }  //抛出 C++ 异常
};//这个函数中使用了 try-catch 处理异常,也即 C++ 异常处理  
void  test1()
{A a1;A a2, a3;try{a2.f1();a3.f2();}catch (int  errorcode){printf(" catch exception,error code:%d\n ", errorcode);}
}// 这个函数没什么改变,仍然采用 try-except 异常机制,也即 SEH 机制  
void  test()
{int *  p = 0x00000000;  //  pointer to NULL  __try{//  这里调用 test1 函数  test1();puts(" in try1 ");__try{puts(" in try2 ");* p = 13;   //导致一个存储异常  puts("  这里不会被执行到  ");}__finally{puts(" in finally ");}puts("  这里也不会被执行到  ");}__except (puts(" in filter 1 "), EXCEPTION_CONTINUE_SEARCH){puts(" in except 1 ");}
}void main()
{puts(" hello ");__try{test();}__except (puts(" in filter 2 "), EXCEPTION_EXECUTE_HANDLER){puts(" in except 2 ");}puts(" world ");
}

上面的例程中,虽然在同一个程序中既有 try-catch 机制,又有 try-except 机制,当然这也完全算得上 SEH 与 C++ 异常模型的混合使用 。但是,请注意,这两种机制其实是完全被分割开的,它们完全被分割在了两个函数的内部。也即这两种机制其实并没有完全交互起来,换句话说,它们还算不上两种异常处理机制真正的混合使用。

打印结果如下:

hello
catch exception, error code : 888
in try
in try
in filter 1
in filter 2
in finally
in except 2
world

二、俩者联合的混合使用

#include "stdafx.h"
#include <iostream>
#include <Windows.h> 
using namespace std;class MyException
{
public:MyException() { printf(" 构造一个 MyException 对象 \n"); }MyException(const MyException& e) { printf(" 拷贝一个 MyException 对象 \n"); }MyException & operator=(const MyException& e) { printf(" 赋值一个 MyException 对象 \n"); }~MyException() { printf(" 析构一个 MyException 对象 \n"); }
};class A
{
public:A() { printf(" 构造一个 A 对象 \n"); }~A() { printf(" 析构一个 A 对象 \n"); }void f1() {}// 注意,这里抛出了一个 MyException 类型的异常对象//throw还触发了一次拷贝构造void f2() { MyException e; throw e; } 
};//这个函数中使用了 try-catch 处理异常,也即 C++ 异常处理
void test1()
{A a1;try{a1.f1();a1.f2();}// 这里虽然有 catch 块,但是它捕获不到上面抛出的 C++ 异常对象catch (int errorcode){printf("catch exception,error code:%d\n", errorcode);}
}// 这个函数没什么改变,仍然采用 try-except 异常机制,也即 SEH 机制
void test()
{int* p = 0x00000000; // pointer to NULL__try{// 这里调用 test1 函数// 注意, test1 函数中会抛出一个 C++ 异常对象test1();puts("in try");__try{puts("in try");*p = 13;puts(" 这里不会被执行到 ");}__finally{puts("in finally");}puts(" 这里也不会被执行到 ");}__except (puts("in filter 1"), EXCEPTION_CONTINUE_SEARCH){puts("in except 1");}
}void main()
{puts("hello");__try{test();}// 这里能捕获到 C++ 异常对象吗?拭目以待吧!__except (puts("in filter 2"), EXCEPTION_EXECUTE_HANDLER){puts("in except 2");}puts("world");
}

打印结果如下:

hello
构造一个 A 对象
构造一个 MyException 对象
拷贝一个 MyException 对象
in filter1
in filter2
析构一个 MyException 对象
析构一个 MyException 对象
析构一个 A 对象
in except 2
world

上层的 main() 函数和 test() 函数采用 try - except 语句处理异常,而下层的 test1() 函数采用标准的 try - catch 语句处理异常,并且,下层的 test1() 函数所抛出的 C++ 异常会被上层的 try - except 所捕获到,即符合了 SEH 异常模型的规则,又同时遵循了 C++ 异常模型的规则。

注意:throw e的调用,是讲e进行抛出,所以发生了一次拷贝构造函数

疑问:

(1)为什么catch没有捕获到而__except却捕获到了?

(2)__finally中的代码为什么没有被执行?

编程时滥用 try-except 和 try-catch 在一起混用,不仅使我们程序的整体结构和语义受到影响,而且也会造成一定的内存资源泄漏,甚至其它的不稳定因素。在 C++ 程序中运用 try-except 机制,只有在顶层的函数作用域中(例如,系统运行库中,或 plugin 的钩子中)才有必要这样做,一般建议俩者不要混合使用

三、编译错误C2713

每个函数只允许一种形式的异常处理,不能在同一函数中使用结构化异常处理(__try / __除外)和C ++异常处理(try / catch)

#include "stdafx.h"
#include <iostream>
#include <Windows.h> 
using namespace std;int main()
{int* p = 0x00000000; // pointer to NULL__try{puts("in try");// 这里是 C++ 的异常处理语法try{puts("in try");*p = 13;puts(" 这里不会被执行到 ");}catch (...){puts("catch anything");}puts(" 这里也不会被执行到 ");}__except (puts("in filter 1"), EXCEPTION_EXECUTE_HANDLER){puts("in except 1");}
}

VC 实现的异常处理机制,不管是 try - except 模型,还是 try - catch 模型,它们都是以函数作为一个最基本“分析和控制”的目标,也即,如果一个函数内使用了异常处理机制, VC 编译器在编译该函数时,它会给此函数插入一些“代码和信息”(代码指的是当该函数中出现异常时的回调函数,而信息主要是指与异常出现相关的一些必要的链表),因此每份函数只能有一份这样的东西(“代码和信息”),故一个函数只能采用一种形式的异常处理规则

四、编译错误C2712

不能在需要对象展开的函数中使用__try

使用 / EHsc时,具有结构化异常处理的函数不能具有需要展开(销毁)的对象。

如果调用使用__event关键字声明的方法,也会发生错误C2712 。由于事件可能在多线程环境中使用,因此编译器会生成阻止操作底层事件对象的代码,然后将生成的代码封装在SEH try - finally语句中。因此,如果调用事件方法并通过值传递类型具有析构函数的参数,则会发生错误C2712。在这种情况下,一种解决方案是将参数作为常量引用传递

#include "stdafx.h"
#include <iostream>
#include <Windows.h> 
using namespace std;class A
{
public:A() { printf(" 构造一个 A 对象 \n"); }~A() { printf(" 析构一个 A 对象 \n"); }void f1() {}void f2() {}
};int main()
{__try{A a1, a2;  //产生异常后会调用析构函数puts("in try");}__except (puts("in filter 1"), EXCEPTION_EXECUTE_HANDLER){puts("in except 1");}
}

 C++ 异常处理模型中,为了能够在异常发生后,保证正确地释放相关的局部变量(也即调用析构函数),它必须要跟踪每一个“对象”的创建过程,这种由于异常产生而导致的对象析构的过程,被称为“ unwind ”,因此,如果一个函数中有局部对象的存在,那么它就一定会存在 C++ 的异常处理机制(即会给此函数插入一些用于 C++ 异常处理“代码和信息”),这样,如果该函数(此时也就是main函数)中在再使用 try-except 机制,就会造成冲突,所以编译器也就报错了。

可能的解决方案:

(1)将需要SEH的代码移动到另一个函数

(2)重写使用SEH的函数以避免使用具有析构函数的局部变量和参数。不要在构造函数或析构函数中使用SEH

(3)无 / EHsc编译

五、其他C2712错误案例及解决方案

#include "stdafx.h"
#include <iostream>
#include <Windows.h> 
#include <string>
using namespace std;inline std::string foo() { return "abc"; }
inline int foo2() { return 612; }class MyClass
{
public:MyClass() { m_val = 0; }MyClass(int param) { m_val = param; }~MyClass() {}int GetVal() { return m_val; }private:int m_val;
};void TestTryExcept_1()
{using namespace std;string str = "666";		// string 是一个类,销毁对象时会调用析构函数,所以会报错__try{// Do anything}__except (EXCEPTION_EXECUTE_HANDLER){cout << "__except" << endl;}// string str;	// 无论放在函数里的什么位置都会导致 C2712 错误
}void TestTryExcept_2()
{using namespace std;// foo()返回的是临时的string对象,// 也就是说,调用foo()时,会生成一个临时的string变量存放返回的数据// 本质上和TestTryExcept_1()是一样的foo();// 和 TestTryExcept_1() 一样使用了 string 类// string retStr = foo();	__try{// Do anything}__except (EXCEPTION_EXECUTE_HANDLER){cout << "__except" << endl;}
}void TestTryExcept_3()
{// 使用了自己定义的类也会报错,因为销毁对象时同样会调用析构MyClass ObjA(612);int ret = ObjA.GetVal();__try{cout << "__try:" << ret << endl;}__except (EXCEPTION_EXECUTE_HANDLER){cout << "__except" << endl;}
}int _tmain(int argc, _TCHAR* argv[])
{TestTryExcept_1();TestTryExcept_2();TestTryExcept_3();return 0;
}

修改之后的案例:

#include "stdafx.h"
#include <iostream>
#include <Windows.h> 
#include <string>
using namespace std;inline std::string foo() { return "abc"; }
inline int foo2() { return 612; }class MyClass
{
public:MyClass() { m_val = 0; }MyClass(int param) { m_val = param; }~MyClass() {}int GetVal() { return m_val; }private:int m_val;
};void TestTryExcept_1()
{using namespace std;string str = "666";cout << "TestTryExcept_1:" << str.c_str() << endl;
}void TestTryExcept_2()
{using namespace std;cout << "TestTryExcept_2:" << foo() << endl;
}void TestTryExcept_3()
{MyClass ObjA(612);int ret = ObjA.GetVal();cout << "TestTryExcept_3:" << ret << endl;}int _tmain(int argc, _TCHAR* argv[])
{// 类对象的创建 不能和__try__except在同一个函数中//using namespace std;//string str = "main, string object";//printf("%s\n", str);__try{TestTryExcept_1();TestTryExcept_2();TestTryExcept_3();}__except (EXCEPTION_EXECUTE_HANDLER){printf("__except\n");}getchar();return 0;
}

上述修改后的代码还存在一个问题,就是在 main 中使用了__try __except 后,就无法创建类对象,也就是像 string 这样的类就无法使用了,要不然就会报错。所以我们再修改一下,把 main 拆成两个函数:

void TestTryExcept_all()
{__try{TestTryExcept_1();TestTryExcept_2();TestTryExcept_3();}__except (EXCEPTION_EXECUTE_HANDLER){cout << "__except" << endl;}
}int _tmain(int argc, _TCHAR* argv[])
{using namespace std;string str = "main, string object";printf_s("%s\n", str);TestTryExcept_all();getchar();return 0;
}

这样将__try __except分到单独一个函数中,就可以解决问题了

相关参考:

Compiler Error C2712

六、try catch模型与__try __except模型不同点之一

try catch不能捕获内存访问错误,__try __except可以捕获内存访问错误,如 int* i=NULL;  *i=1; 这俩行代码

这篇关于SEH与C++ 异常模型在混合使用时注意情况与错误C2712、C2713的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/763938

相关文章

大模型研发全揭秘:客服工单数据标注的完整攻略

在人工智能(AI)领域,数据标注是模型训练过程中至关重要的一步。无论你是新手还是有经验的从业者,掌握数据标注的技术细节和常见问题的解决方案都能为你的AI项目增添不少价值。在电信运营商的客服系统中,工单数据是客户问题和解决方案的重要记录。通过对这些工单数据进行有效标注,不仅能够帮助提升客服自动化系统的智能化水平,还能优化客户服务流程,提高客户满意度。本文将详细介绍如何在电信运营商客服工单的背景下进行

中文分词jieba库的使用与实景应用(一)

知识星球:https://articles.zsxq.com/id_fxvgc803qmr2.html 目录 一.定义: 精确模式(默认模式): 全模式: 搜索引擎模式: paddle 模式(基于深度学习的分词模式): 二 自定义词典 三.文本解析   调整词出现的频率 四. 关键词提取 A. 基于TF-IDF算法的关键词提取 B. 基于TextRank算法的关键词提取

无人叉车3d激光slam多房间建图定位异常处理方案-墙体画线地图切分方案

墙体画线地图切分方案 针对问题:墙体两侧特征混淆误匹配,导致建图和定位偏差,表现为过门跳变、外月台走歪等 ·解决思路:预期的根治方案IGICP需要较长时间完成上线,先使用切分地图的工程化方案,即墙体两侧切分为不同地图,在某一侧只使用该侧地图进行定位 方案思路 切分原理:切分地图基于关键帧位置,而非点云。 理论基础:光照是直线的,一帧点云必定只能照射到墙的一侧,无法同时照到两侧实践考虑:关

使用SecondaryNameNode恢复NameNode的数据

1)需求: NameNode进程挂了并且存储的数据也丢失了,如何恢复NameNode 此种方式恢复的数据可能存在小部分数据的丢失。 2)故障模拟 (1)kill -9 NameNode进程 [lytfly@hadoop102 current]$ kill -9 19886 (2)删除NameNode存储的数据(/opt/module/hadoop-3.1.4/data/tmp/dfs/na

Hadoop数据压缩使用介绍

一、压缩原则 (1)运算密集型的Job,少用压缩 (2)IO密集型的Job,多用压缩 二、压缩算法比较 三、压缩位置选择 四、压缩参数配置 1)为了支持多种压缩/解压缩算法,Hadoop引入了编码/解码器 2)要在Hadoop中启用压缩,可以配置如下参数

Makefile简明使用教程

文章目录 规则makefile文件的基本语法:加在命令前的特殊符号:.PHONY伪目标: Makefilev1 直观写法v2 加上中间过程v3 伪目标v4 变量 make 选项-f-n-C Make 是一种流行的构建工具,常用于将源代码转换成可执行文件或者其他形式的输出文件(如库文件、文档等)。Make 可以自动化地执行编译、链接等一系列操作。 规则 makefile文件

【C++ Primer Plus习题】13.4

大家好,这里是国中之林! ❥前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站。有兴趣的可以点点进去看看← 问题: 解答: main.cpp #include <iostream>#include "port.h"int main() {Port p1;Port p2("Abc", "Bcc", 30);std::cout <<

使用opencv优化图片(画面变清晰)

文章目录 需求影响照片清晰度的因素 实现降噪测试代码 锐化空间锐化Unsharp Masking频率域锐化对比测试 对比度增强常用算法对比测试 需求 对图像进行优化,使其看起来更清晰,同时保持尺寸不变,通常涉及到图像处理技术如锐化、降噪、对比度增强等 影响照片清晰度的因素 影响照片清晰度的因素有很多,主要可以从以下几个方面来分析 1. 拍摄设备 相机传感器:相机传

C++包装器

包装器 在 C++ 中,“包装器”通常指的是一种设计模式或编程技巧,用于封装其他代码或对象,使其更易于使用、管理或扩展。包装器的概念在编程中非常普遍,可以用于函数、类、库等多个方面。下面是几个常见的 “包装器” 类型: 1. 函数包装器 函数包装器用于封装一个或多个函数,使其接口更统一或更便于调用。例如,std::function 是一个通用的函数包装器,它可以存储任意可调用对象(函数、函数

Andrej Karpathy最新采访:认知核心模型10亿参数就够了,AI会打破教育不公的僵局

夕小瑶科技说 原创  作者 | 海野 AI圈子的红人,AI大神Andrej Karpathy,曾是OpenAI联合创始人之一,特斯拉AI总监。上一次的动态是官宣创办一家名为 Eureka Labs 的人工智能+教育公司 ,宣布将长期致力于AI原生教育。 近日,Andrej Karpathy接受了No Priors(投资博客)的采访,与硅谷知名投资人 Sara Guo 和 Elad G