健壮的程序要求,在一个函数内部,所有异常分支都要考虑回收资源,例如内存,若进入异常分支导致函数提前return,需要保证所有资源得到回收,否则会造成资源泄漏。
C语言没有好的解决方案,需要程序员手动在所有异常分支增加释放机制,这样带来两个问题:
一、增加了设计成本,既要保证所有分支兼顾,又要防止释放语句多次调用导致资源释放两次
二、后续对该函数进行改造时,例如新增了一个异常分支,后来者很难注意到这个细节,程序的可维护性不好
C中为了解决这个问题,常用两种方法,一种是封装一个资源清理函数,缺陷是针对每个需要清理资源的函数都需要封装这样一个对应的清理函数,设计不具有通用性的函数不是一个好的设计方式,而C不支持lambda别无他法。另一种方法就是设计一套goto cleanup语句,如下
void func() {
Source source = open_source();
if (wrong) {
goto cleanup;
}
cleanup:
close_source(source);
}
goto语句的特性是,若控制走到label,那么label后面的语句会被执行,因此上面的程序可以保证不管正常分支和异常分支,资源都得到了保护。这种资源清理模式被很多经典C开源程序所使用,从linux内核到isc的dns、dhcp等应用层开源软件。
c++提供RAII机制,在任何异常分支甚至exception上下文中都可以保证资源释放问题,例如上面的代码在c++中可以如下设计,通过一个guard类在析构中释放资源: