工具书类
下面的文章很好地介绍了一些编写健壮通用组件的问题:
D.亚伯拉罕:``通用组件“”中的异常安全,最初是发布于M.Jazayeri,R.Loos,D.Musser(编辑):通用编程,程序。达格斯图尔研讨会,计算机讲稿科学。体积。1766
指导方针
我应该在什么时候使用异常?
简单的答案是:“只要语义和异常的性能特征是适当。“
一个经常被引用的准则是问自己这个问题这是一个异常(或意外)的情况?”本指南有一个吸引人的戒指,但通常是一个错误。这个问题是一个人的“例外”是另一个人的``expected“”:当您仔细查看术语时区别消失了,你没有任何指导方针。之后总之,如果您检查错误条件,那么在某种意义上您希望它发生,否则检查就是浪费代码。
一个更合适的问题是:“我们想要堆栈吗?”在这里放松?”因为实际处理异常是可能比执行主线代码慢得多,你还应该问:“我能负担得起在这里展开堆栈吗?”对于例如,执行长计算的桌面应用程序可能会定期检查用户是否按下了取消按钮。引发异常可能会允许该操作被优雅地取消。另一方面,它会可能不适合投掷手柄例外因为它可能有一个显著的性能影响。上述指南有一点道理:在时间关键型代码中,抛出例外情况应是例外,而不是规则。
我应该如何设计我的异常类?
- 从派生异常类
标准::异常
.除非非常罕见您无法负担虚拟机成本的情况表,标准::异常
使…合理异常基类,当通用时,允许程序员无需借助捕捉(…)
。有关的更多信息捕捉(…)
,请参见下文。
-
使用事实上的继承。这个这是安德鲁·柯尼格(Andrew Koenig)的见解。使用虚拟继承从异常的基类防止歧义万一有人抛出从具有基的多个基派生的异常通用类:
#包括<iostream>结构my_exc1:std::异常{char const*what()const throw();};结构my_exc2:std::异常{char const*what()const throw();};构造your_exc3:my_exc1,my_exc2{};整型main(){尝试{抛出您的_exc3();}catch(std::exception const&e){}catch(…){std::cout<<“哎哟!”<<std::endl;}}
上面的程序打印“哎哟”
因为C++运行时无法解析哪个例外
要在第一个实例中匹配的catch子句。
-
不要嵌入std::string对象或其复制构造函数的任何其他数据成员或基类可能引发异常。这可能直接导致std::terminate()在抛出点。同样,这是一个坏消息使用其普通构造函数的基或成员的想法可能会扔,因为,虽然对你来说不一定致命程序,您可以报告与预期不同的异常来自通过表达包括建筑例如:
抛出some_exception();
有多种方法可以避免复制字符串对象复制异常时,包括嵌入异常对象中的固定长度缓冲区,或管理字符串通过引用计数。然而,考虑下一个在采用这两种方法中的任何一种之前。
- 格式化
什么()
上的消息需要,如果你觉得你真的必须格式化消息。格式化异常错误消息通常是可能引发例外。这项手术最好推迟到堆栈展开已经发生,并且可能释放了一些资源。在这种情况下,保护您的什么()
函数与捕获(…)
块,以便在格式化代码投掷
- 别担心太关于
什么()
消息。很高兴有一个程序员有机会理解的信息,但你不太可能写出相关的用户-a点的可理解错误消息引发异常。当然,国际化是超出了异常类作者的范围。彼得·迪莫夫生成关于正确使用什么()
字符串用作表的键错误消息格式化程序的个。现在如果我们能得到标准化的什么()
异常字符串由标准库引发。。。
- 公开有关原因的相关信息错误在异常类的公共接口中。固定在
什么()
消息可能会意味着你忽视了暴露某人可能需要,以便为用户提供连贯的信息。对于例如,如果异常报告数值范围错误,重要的是要有相关的实际数字作为数字在异常类的公共接口中错误报告代码可以智能处理他们。如果您只公开这些内容的文本表示中的数字什么()
字符串,你会创造生命对于需要做更多事情的程序员来说非常困难(例如减法),而不是哑输出。
- 使您的异常类免受双重破坏如果可能的话。不幸的是,一些流行的编译器偶尔会导致异常要销毁两次的对象。如果你能安排无害化(例如通过将删除的指针归零)您的代码将更加稳健。
程序员错误怎么办?
作为开发人员,如果我违反了我正在使用的库,我不想堆栈展开。我想要的是堆芯转储或等效物-一种检查状态的方法程序在检测到问题的确切位置。这通常意味着资产()
或者类似的东西它。
有时需要有弹性的API几乎可以抵抗任何形式的客户虐待,但这种方法通常会带来很大的成本。例如,它通常要求跟踪客户端使用的每个对象以便检查其有效性。如果你需要那种通常可以在更简单的API。不过,要注意减半措施。一个API承诺对一些人有弹性,但并非所有虐待都是灾难的邀请。客户将开始依赖保护和他们的期望将增长到接口中未受保护的部分。
Windows开发人员注意事项:很遗憾,大多数Windows编译器使用的本机异常处理当您使用资产()
事实上,其他人也是如此程序员错误,如分段错误和零分错误。其中一个问题是,如果您使用JIT(实时)调试,将附带异常-在调试器出现之前展开,因为捕捉(…)
会抓到这些不是真正的C++例外情况。幸运的是,有一个简单但鲜为人知的变通方法,即使用以下咒语:
extern“C”void strate_to_debugger(无符号int,EXCEPTION_POINTERS*){投掷;}外部“C”void(*old_translator)(无符号,EXCEPTION_POINTERS*)=_set_se_translator(直接调试程序);
如果SEH是从内部提出的,则此技术不起作用catch块(或从catch块中调用的函数),但它仍然消除了绝大多数JIT屏蔽问题。
我应该如何处理异常?
通常,处理异常的最佳方法是不处理完全没有。如果你能让他们通过你的代码允许析构函数处理清理,则您的代码将清洁剂。
避免捕捉(…)
什么时候可能的
不幸的是,除Windows还包含非C++“异常”(例如线程取消)到C++EH机器中,有时没有对应于_设置_翻译程序
上述黑客攻击。这个结果是捕捉(…)
可以有以下效果在以下情况下发出一些意外的系统通知恢复不可能看起来像抛出的C++异常从一个合理的地方,使通常的保险箱失效析构函数和catch块已生效的假设在展开过程中确保程序不变量的步骤。之后,我不情愿地将这一点让给了希勒尔·西姆斯(Hillel Y.Sims)新闻组中的许多长时间辩论:直到所有操作系统如果每个异常都派生自标准::异常
所有人都换了catch(std::exception&)
对于捕捉(…)
,世界会变得更好。
有时,捕捉(…)
,仍然是最多的适当的模式,尽管与操作系统/平台设计选择。如果你不知道什么样的可能会抛出异常必须停止放松可能仍然是你最好的选择。一个明显的地方这种情况发生在语言边界。