//========================================================================
//TITLE:
// 如何写优雅的代码(1)——灵活使用goto和__try:评论反馈
//AUTHOR:
// norains
//DATE:
// Tuesday 21-July-2009
//Environment:
// WINCE5.0 + VS2005
//========================================================================
本文会引起不少的争议,也算意料之中,毕竟文无定法。不过,有些朋友的观点我确实也考虑过,但并不是十分赞同。又因为直接回复格式实在无法统一,故在此撰文一并作答。
1.feelapi 发表于2009年7月16日 17:48:48 IP:举报
VC8.0以后就不用担心SEH冲突的问题,都统一了,可以使用/EHa编译选项,就没问题了
我用vs2005测试如下代码,还是不行:
int WINAPI WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPTSTR lpCmdLine,
int nCmdShow)
{
__try
{
std::vector<BYTE>::iterator iter;
}
__finally
{}
return 0;
}
我设置的方式如下:property-->c/c++--->Code Generation--->Enable C++ Exceptions,选择:yes with SEH exceptions(/EHa)。
编译出错信息:Cannot use __try in functions that require object unwinding
不知道是不是我设置的问题?
2.titilima 发表于2009年7月17日 11:41:01 IP:举报
do-while (0) 可以更优雅地解决这个问题,而且不会带来 SEH 的额外开销。
xylophone21 发表于2009年7月20日 12:55:49 IP:举报
do { break; }while(0)
jack1003 发表于2009年7月18日 9:45:02 IP:举报
探讨下:楼主这样实现也可以,但用TRY 总觉得有点费解,看看这样实现怎么样?
BOOL ReadDeviceBuf()
{
EnterCriticalSection(&g_csBuf);
bool bOk = true;
do { //打开驱动设备 HANDLE hDev = CreateFile(TEXT("DEV1:"), FILE_WRITE_ATTRIBUTES, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL|FILE_FLAG_WRITE_THROUGH, NULL);
if(hDev == INVALID_HANDLE_VALUE)
{ bOk = false; break; } //获取驱动设备的缓存大小 DWORD dwSize = 0; if(DeviceIoControl(hDev,IOCTRL_BUFFER_SIZE,NULL,0,&dwSize,sizeof(dwSize),NULL,NULL) == FALSE)
{ bOk = false; break; } //分配缓存 g_pBuf = new BYTE[dwSize]; if(g_pBuf == NULL) { bOk = false; break; } //从驱动中读取数据 if(DeviceIoControl(hDev,IOCTRL_GET_BUFFER,NULL,0,&g_pBuf,dwSize,NULL,NULL) == FALSE)
{
delete []g_pBuf;
g_pBuf = FALSE;
bOk = false;
break;
}
} while (false);
CloseHandle(hDev);
LeaveCriticalSection(&g_csBuf);
return bOk ? TRUE:FALSE;
}
这样确实也可以,效率也比__try高,唯一的瑕疵是“词不达意”——毕竟while这关键字是用来表达循环的。这也是我为什么不选用while的原因,仅仅是个人喜好而已。
3.kinsan 发表于2009年7月17日 16:15:43 IP:举报
我面试人 只要有程序里写goto的 一律刷掉 goto 不容易掌握流程 程序规模越大就越难控制 就你那么几行代码 能说明什么? 世人鄙弃goto 不是没有理由的
看来微软wince团队的那些巨擘想进你们公司都不容易啊,因为wince的源代码中有太多的goto出自于这些牛人之手了...所以齐刷刷被你刷掉了... :-) 你可以查看一下wince5.0的public部分代码,一共有5086处用到了goto,这还没算private等其它的文件夹....
还有一些网友对此的看法:
kryso 发表于2009年7月17日 18:01:31 IP:举报
只能说你的教条主义严重.不容易掌握流程是代码编写能力的问题,证明你的水平没到家.
jzkdl 发表于2009年7月18日 15:00:29 IP:举报
我认为goto本身没有错~~ 对于goto来说更适合用在编写较底层的驱动代码~~或者简单函数里面~毕竟goto本身就是汇编JMP指令的代表~用goto有时候更容易理解计算机的运行原理~~ 试想一下,现在各种系统的内核不是还使用汇编吗~~ 如果你硬要拿goto和面向对象一起讨论~~我不发表意见~ 我个人非常支持作者的观点~~用goto只是为了优雅~~ 这里不是讨论到底应不应该用goto~~
wlzqi 发表于2009年7月18日 22:04:09 IP:举报
"我面试人 只要有程序里写goto的 一律刷掉 goto 不容易掌握流程 程序规模越大就越难控制 就你那么几行代码 能说明什么? 世人鄙弃goto 不是没有理由的" ---------你还太浅了。
ayave 发表于2009年7月18日 22:09:35 IP:举报
“我面试人 只要有程序里写goto的 一律刷掉 goto 不容易掌握流程 程序规模越大就越难控制 就你那么几行代码 能说明什么? 世人鄙弃goto 不是没有理由的” 哪个说的世人摒弃goto,linux内核满篇的goto, 我不认为linux内核就是垃圾。
cppmobile 发表于2009年7月19日 23:05:57 IP:举报
不需要盲目的去鄙弃一个东西,只要用得其所就可以用。
4.morphia 发表于2009年7月18日 7:07:55 IP:举报
为了个C 对象就改用goto!!非常糟糕! 真正的解决办法:把你的那些HANDLE用对象封装起来,像std::auto_ptr一样,然后还有你的与CriticalSection的东东都封装起来,在对象destroy的时候释放资源,使用_set_se_translator将SEH的异常封装成对象抛出,使用try catch处理各种不同类型的异常,才叫完美解决。 你这种解决办法,一两百行代码还行,代码多了你就完蛋了。
说实话,函数的目的就是精简。如果函数代码超过一两百行,再不分割为各自小函数的话,估计就是思路有问题了。
5.morphia 发表于2009年7月18日 7:26:07 IP:举报
另外补充,try catch应该在该函数之外捕获异常,而当你的设备打开,读取,确定大小失败的时候就应该抛置你自己定义的异常,这时函数直接因为异常而退出,而因为你的HANDLE和CRITICALSECTION被对象封装,并且在对象释放的时候会自动释放HANDLE和LEAVE CRITICALSECTION,所以异常不会导致资源没有被释放。具体的,参见《More Effective C 》里有关章节。
cplusplus_zk 发表于2009年7月18日 13:54:29 IP:举报
俺认为,像里面那个临界区的处理,还是封装成一个类更优雅。用goto我可以容忍,但绝对不应该推荐,特别是很多C 并不是很精通的程序员很容易收到误导的。
barsteng 发表于2009年7月20日 17:13:49 IP:举报
同意morphia和cplusplus_zk 的观点,不知道楼主是否会考虑用类封装资源的管理更优雅?而且更容易被C developer理解我post了一个实现,可惜格式缩进什么的都被过滤了,呵呵
用类来做互斥量的确是个好办法,比如:
class CLock
{
public:
CLock(LPCRITICAL_SECTION pcsLock):
m_pcsLock(pcsLock)
{
EnterCriticalSection(m_pcsLock);
};
virtual ~CLock()
{
LeaveCriticalSection(m_pcsLock);
};
private:
LPCRITICAL_SECTION m_pcsLock;
};
调用的时候:
BOOL ReadDeviceBuf()
{
CLock lock(&g_csBuf);
...
}
这在当前函数例子中表现的确很好,也在大部分情况下很优雅。但如果函数需要多次进入临界点,那么就不显得那么美好了,如:
static DWORD MonitorProc(LPVOID pParam)
{
CLock lock1(&g_csBuf);
//做一些事情
~lock1(); //显式调用析构函数
//做一些事情,比如通知别的进程数据有改变
CLock lock2(&g_csBuf);
//做一些事情
~lock2(); //显式调用析构函数
}
姑且不说调用析构函数和直接调用LeaveCriticalSection无异,就说显式调用析构函数,也属于极少使用的技法。这是这个方法的唯一瑕疵。当然,我举的这个反例或多或少是有点吹毛求疵了。
6. Analyst 发表于2009年7月20日 21:45:25 IP:举报
不管是用goto还是__try __finally都是糟糕的C 写法,只有C程序才会这么写。 正确的C 写法是这样的:
void ReadDeviceBuf()
{
HANDLE hDev;
try{
CriticalSectionGuard lock((&g_csBuf);
hDev = CreateFile(TEXT("DEV1:"), FILE_WRITE_ATTRIBUTES, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL|FILE_FLAG_WRITE_THROUGH, NULL);
if(hDev == INVALID_HANDLE_VALUE)
throw SysException(GetLastError());
DWORD dwSize = 0;
if(DeviceIoControl(hDev,IOCTRL_BUFFER_SIZE,NULL,0,&dwSize,sizeof(dwSize),NULL,NULL) == FALSE)
throw SysException(GetLastError());
//....
CloseHandle(hDev);
}
catch(SysException& e)
{
CloseHandle(hDev);
throw e;
}
}
这是一个看起来很美的写法,但实际上还是有问题的。
写代码,有一个原则是:Input once,output once. 翻译成中文,也就是一进一出相对应。而这代码偏偏违反了这个原则。
为了方便,我在代码加上标签:
void ReadDeviceBuf()
{
HANDLE hDev;
try{
CriticalSectionGuard lock((&g_csBuf);
hDev = CreateFile(TEXT("DEV1:"), FILE_WRITE_ATTRIBUTES, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL|FILE_FLAG_WRITE_THROUGH, NULL);
if(hDev == INVALID_HANDLE_VALUE)
throw SysException(GetLastError());
DWORD dwSize = 0;
if(DeviceIoControl(hDev,IOCTRL_BUFFER_SIZE,NULL,0,&dwSize,sizeof(dwSize),NULL,NULL) == FALSE)
throw SysException(GetLastError());
//....
A:
CloseHandle(hDev);
}
catch(SysException& e)
{
B:
CloseHandle(hDev);
throw e;
}
}
如标签A和B所示,这两处地方都属于同义资源释放。换句话说,这两部分的代码基本上是一模一样的,否则就达不到释放资源的目的。
那么很显然,会出现这两个问题:
1).如果释放资源的代码行数比较多,而A和B必须是同样的代码,由此造成函数的体积膨胀加快,显得极为臃肿。
例如:
void ReadDeviceBuf()
{
//....
A:
delete [] pBuf1;
delete [] pBuf2;
NotifyError();
CloseHandle(hDev);
}
catch(SysException& e)
{
B:
delete [] pBuf1;
delete [] pBuf2;
NotifyError();
CloseHandle(hDev);
throw e;
}
}
2).当代码持续膨胀,后续的修改者又不是作者本人的时候,很难保证标签A和B的代码一致。如果不一致,甚至只是少些一两段语句,那么肯定会造成资源泄漏。如果长时间运行,很有可能有异常发生。
另外,更正一点,__try __finaly并不是什么糟糕的C写法,这个是SEH,由windows所特有支持的机制(所以不具备移植性)。至于__try __finaly是否是糟糕的做法,我只能说的是,在C#的语言层面已经支持和该机制类似的方法。对于号称去掉C/C++繁琐和难懂等弊端的现代C#语言,也支持该C++所不具备的特性,也许能说明一些问题吧?
分享到:
相关推荐
if ( (tmptm1 < _BASE_YEAR - 1) || (tmptm1 > _MAX_YEAR64 + 1) ) goto err_mktime; } /***** HERE: tmptm1 holds number of elapsed years *****/ /* * Calculate days elapsed minus one, in the given ...
[ 3.968638] [mmc]: sdc2 set ios: clk 25000000Hz bm PP pm ON vdd 3.3V width 1 timing LEGACY(SDR12) dt B [ 3.968734] [mmc]: mclk 0xf1c20090 0xc100000b [ 3.989421] Bluetooth: BNEP filters: protocol ...
(7) 结构化程序设计方法的主要原则可以概括为自顶向下、逐步求精、______和限制使用goto语句。 答:模块化 (8) 软件的调试方法主要有:强行排错法、______和原因排除法。 答:回溯法 (9) 数据库系统的三级模式分别...
C:\Progra~1\WinRAR\winrar a -r d:\数据库备份\new_ylqfs%Date:~0,4%%Date:~5,2%%Date:~8,2%.rar d:\数据库备份\new_ylqfs%Date:~0,4%%Date:~5,2%%Date:~8,2%.dmp del d:\数据库备份\new_ylqfs%Date:~0,4%%Date:~5...
[ 3.968638] [mmc]: sdc2 set ios: clk 25000000Hz bm PP pm ON vdd 3.3V width 1 timing LEGACY(SDR12) dt B [ 3.968734] [mmc]: mclk 0xf1c20090 0xc100000b [ 3.989421] Bluetooth: BNEP filters: protocol ...
优秀的,完整的BIOS 代码 page ,132 title . PROCESSOR_TIMER_PARITY_REFRESH_NMI TEST ;*****************************************************************; ;********************************************...
On Error GoTo Err_cmdClose_Click If MsgBox("是否确定退出系统?", vbQuestion + vbYesNo, "系统提示") = vbYes Then DoCmd.Quit Else Exit Sub End If Exit_cmdClose_Click: Exit Sub Err_cmdClose_Click:...
显示LO和温度计并自动关机 bsf OperateFlag,temp_lo goto MeasureTemperEnd CheckHighLimit: movlw 01h subwf TempValueInteger,w btfss status,c goto Count_Tf btfss status,z goto TemperMax404 movlw 091h ;191...
Python 前端,它使添加、删除和编辑发射器变得相对容易,并且还可以将功耗数据以与使用的格式相同的格式记录到一组日志文件中。 手动的 请参阅以获取使用此代码的指南。 相关项目 代码,在 Nanode / Arduino 上运行...
'返回待机点 *ST: SELECT CASE SI2() CASE 0 GOTO *ST CASE 1 GOTO *MQB_A CASE 2 GOTO *MQB_B CASE 16 GOTO *HOME CASE ELSE GOTO *ST END SELECT GOTO *ST ' *MQB_A: SO(30)=1 ASPEED 30 SPEED 100 MOVE P,P5 WAIT...
一个简单的goto人脸识别 前提条件 它接受图像和视频进行培训。 因此,您可以: 创建目录并放置属于某个特定人的所有图像和视频 将目录重命名为相应的人名 与其余的人重复 笔记: 图片或视频应仅包含相应人物的脸 ...
#define REG_HR_PM (1) /* PM/AM bit in 12h mode */ #define REG_DW 0x03 #define REG_DT 0x04 #define REG_MO 0x05 #define REG_YR 0x06 #define RTC_SECTION_LEN 7 /* i2c configuration */ #define I2C_ADDR 0...
Scripts_Web_UI_Autotest简介: 在前端 UI 自动化测试中,提供类抓包工具的拦截 url 断点处理和 mock 数据等功能, 并集成埋点自动化测试,让你的前端自动化彻底摆脱后端和抓包工具的束缚,直接起飞。技术栈: node.js...
麦克纳姆_机器人 控制全方位机器人的代码
1、增加了USB/UVC摄像头的处理(*请问你们是否加入到版本库里面去?还是里面拿到的开发版本里面后,我们从外网git的代码没有????*): switch((v4l2_sensor_type)getSensorType()){ case V4L2_SENSOR_TYPE_YUV: ...
r40_tinav2.1_最终验证通过_使用CB-S来验证OV5640有横条纹fpscamera+SPI2.0成功_20171114_1443没有外层目录.7z 开发板:CB-S 1、(可选修改/调试技巧:) 除了ov5640.c之外,其它的驱动都不编译,节省编译时间!...
c++中的goto(csdn)————程序
正确结果:等效的三地址代码序列 L1: t1 := a3 + 15 if t1 > 10 goto L2 goto L0 L2: if x2 = 7 goto L3 goto L1 L3: if y < z goto L4 goto L1 L4: t2 = x * y t3 = t2 / z y = t3 goto L3 goto L1 L0: // S.next
vfe_dbg(0,"num = %d , sd_0 = %p,sd_1 = %p\n",input_num,dev->ccm_cfg[input_num]->sd,dev->ccm_cfg[input_num-1]->sd); goto snesor_register_end; } if((dev->dev_qty > 1) && (input_num+1<dev->dev...