3

除了 之外,我的异常类还应该提供公共领域的详细信息what()吗?

例如boost::property_tree::ptree_bad_path,考虑可能会给出一条消息:

"No such node (mynode.value1)"

访问路径 ( "mynode.value1") 的唯一方法是解析字符串。是否有反对添加额外的公共字段来承载此类信息的论点,即:

class ptree_bad_path : public ptree_error {
public:
  const std::string path; // <- additional detail by public field
....

这种方法有缺点吗?

4

4 回答 4

2

您的异常类应包含处理错误所需的所有信息。这通常意味着用出错的地方和任何必要的上下文来标记它。将路径存储在异常类中是个好主意。

这种方法有缺点吗?

尽量避免让成员自己抛出,因为他们会调用 std::terminate。

于 2012-11-12T10:44:20.297 回答
2

terminate从理论上讲,如果您同时有两个未处理的异常,您就有可能拥有您的程序。然而,这是一种相当罕见的情况。

  • 在准备异常期间抛出:很好(尽管您不会得到预期的异常)
  • 在异常复制期间抛出(通常被忽略,可避免):崩溃
  • 放卷时投掷:crash
  • 处理异常时抛出(catch):很好(毕竟重新抛出不同的异常是很常见的)

因此,这里可避免的风险是,如果您的异常的复制构造函数可能碰巧抛出。通过将状态移到shared_ptr异常中包含的状态来逃避问题是微不足道的。它使副本有点“特殊”(因为它们与原件共享它们的状态),但如果它被正确记录,它不应该引起任何悲伤。

更大的风险是在堆栈展开期间。但是,它仅在析构函数抛出时才会发生。

就个人而言,我使用的例外包括:

  • 一个错误代码(为了让 API 正确显示/编码,所有错误消息都映射到一个代码,它对中文/韩文/日文有帮助,真的)
  • 一条日志消息,带有一些详细信息(导致问题的项目的 ID/名称,翻译另一个异常时的原始错误,有什么帮助!)
  • 抛出异常的函数/文件/行
  • 抛出异常的时间
  • 附加说明(在堆栈展开期间“即时”附加)
  • 完整的回溯(使用 Linux 特定功能)

这里唯一有争议的一点是on the fly bit,因为它可能会有效地崩溃。另一方面,我在服务器上工作,因此很容易(并且紧急)修复崩溃,并且在过去的 5 年中,我非常小心,没有导致崩溃(这种方式;))。

请注意,此方案显然仅在您很少使用异常时才可用。如果您经常为每个任务抛出十几个异常,那么性能可能会令人无法接受。另一方面,主要编译器使用的零成本模型已经严厉惩罚了异常路径,所以......

于 2012-11-12T14:41:15.727 回答
1

这只是我的意见,但如果您要抛出异常,您可能需要确保它有足够的信息让您知道(a)导致异常的原因,以及(b)抛出异常的位置。

您可能(希望)不会向最终用户显示异常,因此记录异常成为纯粹启用/提高可支持性的东西。因此,从开发的角度来看,您基本上希望处于尽可能多地了解发生的事情的位置。

当然你是对的,你在这里走的是一条细线。您不希望在异常处理中包含如此复杂的代码,以至于它冒着抛出自己的异常的风险!

于 2012-11-12T10:47:12.383 回答
0

您的异常可能会携带尽可能多的信息,因为它们的捕获器已准备好(并愿意)使用。如果您的特定应用程序可以使用这些附加信息,您可以放心地编写自己的异常类。在任何情况下,所有异常类都应该继承自std::exception,以确保catch不期望自定义异常的子句能够正常工作。

另一个问题是在库中公开这些类以供第三方客户端使用。在这种情况下,您应该仔细考虑这些附加信息的好处是否超过了附加界面带来的麻烦,甚至可能根本不使用它。

编辑:正如Pubby 所说,你的异常类不应该抛出以避免不受欢迎的调用std::terminate()。一般来说,任何与异常相关的代码都不应该抛出,这包括任何类的析构函数。

于 2012-11-12T10:46:05.253 回答