我的一位朋友向我提出了这个问题。我被困住了因为我不善于使用异常。请记住,我们都在使用C ++的工作环境中工作,但在C传统中进行错误处理。他的问题是这样的:
功能A
调用B
,然后调用C
。从C
抛出异常,该异常的catch块位于A
。在致电B
之前,C
中获得的资源会发生什么变化?我们如何清理它们?我的回答是使用RAII。但即使我说出来,我也知道它不会起作用。我们有很多以C模式编写的代码库。在代码中我没有看到自动指针等。资源不一定包含在类中。即使它们是,在大多数情况下,析构函数仍留给编译器。简而言之,一切都是手动完成的。
真正的问题是如何通过巨大的代码库来实现从C错误处理到异常的转换?我的朋友提出的问题只是当您在C错误处理方面有所建议并且想知道如何从那里迁移到异常时可以提出的问题之一。
答案 0 :(得分:5)
有一种技术由Andrei Alexandrescu和Joshua Lehrer开发,称为Scope Guard,它提供了一种技术,可以在分配对象时立即提供“范围退出”代码,并在此时打开它的范围
D编程语言实际上是标准语言。
boost库有一个名为Scope Exit的增强功能。 如果您的代码不是异常安全的,因为函数B看起来不是,如果抛出任何东西,它将无法正常处理。
析构函数将在异常后展开调用堆栈时运行。这就是为什么你必须确保他们自己不会抛出任何例外。
答案 1 :(得分:2)
真正的问题是如何通过巨大的代码库来实现从C错误处理到异常的转换?
不,真正的问题在于,由于您使用C ++进行编程,因此您应该很快完成此操作。即使您自己的代码没有抛出异常, 标准库和运行时系统中的某些代码也可能抛出 (new
,dynamic_cast
),当然,你使用的任何第三方软件都可能很好。
我同意Scope Guard可能是您在事后补充时添加异常安全的最佳选择。但是,只要你不自己抛出异常,不要以为你不需要这样做就不要欺骗自己。 您使用的是具有例外情况的语言 ,因此您最好尽快开始处理它们。
答案 2 :(得分:1)
无论如何,您应该迁移到RAII,因为当您更改控制流程时,除其他外,您不能使用它。你需要升级。例外意味着您无法对可能存在异常的任何函数进行半升级。
答案 3 :(得分:1)
C ++不支持最终来管理块末尾的最后一分钟。
当函数 C 抛出异常时,它会从函数返回。这意味着所有局部变量都将被销毁 然后程序将返回 B 中的代码,它将检查catch块,看到有非,然后跳回到函数A.同样,B中的所有局部变量都将被释放。
你必须记住你在使用C ++ ....你必须管理你的对象。所以,如果你有对象只是想在 B (带指针或其他)中自由地思考,那么这是一个糟糕的代码设计,它们不会被释放。
如果您在 B 中知道 C 中可能存在异常,则可以在那里放置一个catch,然后重新抓回捕获的异常。
最接近finally
块的是使用catch(...)
并使用它来释放内存...但只有在实际存在异常时才会输入它,因此它与finally
不同{1}}
try {
}
catch(...) {
// free stuff affected by an exception here
}
如果你想完全像finally
一样工作,你可以这样做:
try {
//Do stuff
goto finally
}
catch(...) {
finally:
// free stuff affected by an exception here
}
对于所有遇到goto
问题的人来说......它仍然是一个命令,这是一个完美的例子,没有break
或continue
或某些内联函数可以取代goto
。
更多关于goto
的信息答案 4 :(得分:0)
您的朋友是完全正确的,这也是Google内部不使用例外的原因之一。请参阅http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Exceptions(并打开小箭头)以解释问题。
答案 5 :(得分:0)
摘自我遗弃的主页...
不重复细节 异常固有的问题 处理(1)我只想说明 主要思想:复杂的部分不是 产生异常的地方 (抛出)或异常的地方 处理(捕获),但在方法 异常通过的所有类 通过几乎异步 堆栈展开过程。
注意方法如何 写的比较容易避免 在例外的情况下丢失资源 (使用auto_ptr和类似的类) 但事情要复杂得多 一个考虑到逻辑状态 对象或其他对象 在投掷之前操纵。在里面 最严格的例外安全意义 方法应该成功还是成功 作为一个无操作者:换句话说 被调用对象的逻辑状态 (和其他涉及的对象)应该是 如果抛出异常则相同:否 差异应该是可见的 所有参与者的公共接口 类。
...
(1)有关详细说明,请阅读 文章“异常处理:错误 汤姆卡吉尔的“安全意识” 可用online或有趣的 收集问题/解决方案 该文章在USENET上生成 可以在“Exceptional C ++“作者:Herb Sutter ISBN 0-201-61562-2 =
答案 6 :(得分:0)
很高兴你有例外。考虑一下您的示例中的错误返回代码会发生什么。函数C
返回错误代码。 B
,虽然天真,却吞下它。能够处理它的呼叫者A
甚至没有被告知错误。
你最好的选择是从C直接转到C ++ 0x。它提供了一个新功能,lambda。这些是匿名函数,在其他函数中定义。您可以将C清理代码放在那里,并通过“scopeguard”对象调用它。 Example(我跳过宏)