注意:我没有扮演魔鬼的拥护者或类似的东西 - 我真的好奇,因为我不在这个阵营自己。
标准库中的大多数类型都具有可以抛出异常的变异函数(例如,如果内存分配失败)或者可以抛出异常的非变异函数(例如,超出边界的索引访问器)。除此之外,许多自由函数都可以抛出异常(例如operator new
和dynamic_cast<T&>
)。
你如何几乎在&#34;我们不使用例外&#34;
的背景下处理这个问题?你是否试图从不调用可以抛出的函数? (我无法看到它是如何缩放的,所以如果是这种情况,我非常有兴趣听听你如何实现这一目标)
您对标准库投掷是否合适?您对待&#34;我们不使用例外&#34; as&#34;我们从来没有从我们的代码中抛出异常,我们从来没有捕获其他的异常>代码&#34;?
您是否通过编译器开关完全禁用异常处理?如果是这样,标准库的异常抛出部分如何工作?
编辑您的构造函数,它们是否会失败,或按惯例执行使用具有专用init函数的两步构造,该函数可在失败时返回错误代码(构造函数可以& #39; t),或者你还做别的什么?
编辑问题开始后1周进行轻微澄清......以下评论和问题中的大部分内容都集中在为什么方面的异常vs&# 34;别的东西&#34;。我的兴趣不在于此,但当你选择做什么&#34;其他&#34;,如何你处理的标准库部分抛出异常?
答案 0 :(得分:41)
我会为自己和世界的角落回答。我写了c ++ 14(一旦编译器有更好的支持将是17)延迟关键的财务应用程序,处理庞大的金钱,并且不会下降。规则集是:
内存已合并并预先分配,因此初始化后没有malloc调用。数据结构要么是不朽的,要么是可以复制的,因此几乎不存在析构函数(有一些例外,例如范围保护)。基本上,我们正在做C +类型安全+模板+ lambda。当然,通过编译器开关禁用异常。至于STL,它的好部分(即:algorithm,numeric,type_traits,iterator,atomic,......)都是可用的。异常抛出的部分很好地与运行时内存分配部分和半OO部分重合,因此我们可以一次性去掉所有的内容:流,除了std :: array,std :: string之外的容器。 / p>
为什么这样?
答案 1 :(得分:19)
在我们的例子中,我们通过编译器禁用异常(例如,-fno-exceptions
用于gcc)。
在gcc的情况下,他们使用名为_GLIBCXX_THROW_OR_ABORT
的宏,其定义为
#ifndef _GLIBCXX_THROW_OR_ABORT
# if __cpp_exceptions
# define _GLIBCXX_THROW_OR_ABORT(_EXC) (throw (_EXC))
# else
# define _GLIBCXX_THROW_OR_ABORT(_EXC) (__builtin_abort())
# endif
#endif
(您可以在最新的gcc版本的libstdc++-v3/include/bits/c++config
中找到它。)
然后,你必须处理抛出异常中止的事实。您仍然可以捕获信号并打印堆栈(SO上有一个很好的答案可以解释这一点),但您最好避免发生这种情况(至少在发布中)。
如果你想要一些例子,而不是像
那样try {
Foo foo = mymap.at("foo");
// ...
} catch (std::exception& e) {}
你可以做到
auto it = mymap.find("foo");
if (it != mymap.end()) {
Foo foo = it->second;
// ...
}
答案 2 :(得分:18)
我还想指出,在询问不使用例外情况时,有一个关于标准库的更一般的问题:你当你在其中一个“我们穿上”时使用标准库“使用例外”阵营?
标准库很重。在一些“我们不使用例外”阵营中,例如许多GameDev公司,使用更适合STL的替代品 - 主要基于EASTL或TTL。这些库无论如何都不使用异常,因为第八代控制台没有很好地处理它们(甚至根本没有)。对于尖端的AAA生产代码,无论如何异常都太重了,所以在这种情况下这是一个双赢的场景。
换句话说,对于许多程序员来说,关闭异常是完全没有使用STL。
答案 3 :(得分:10)
注意我使用例外......但我不得不这样做。
您是否试图永远不会调用可以投掷的功能? (我无法看到这种规模如何,所以如果是这样的话,我很想知道你是如何实现这一目标的)
这可能是不可行的,至少是大规模的。许多函数可以抛出,避免它们完全削弱你的代码库。
您对标准库抛出是否正常,并且您将“我们不使用异常”视为“我们从不从代码中抛出异常而我们从未从其他代码中捕获异常”?
你几乎要对它好一点......如果库代码会抛出异常并且你的代码不会处理它,则终止是默认行为。
您是否通过编译器开关完全禁用异常处理?如果是这样,标准库的异常抛出部分如何工作?
这是可能的(在某些项目类型的某些时候很流行的那一天);编译器可以/可能支持这一点,但是您需要查阅他们的文档,了解结果的可能性和可能性(以及在这些条件下支持的语言功能)。
通常,当抛出异常时,程序将需要中止或以其他方式退出。一些编码标准仍然需要这一点,我想到了JSF编码标准(IIRC)。
“不使用例外”的人的一般策略
大多数功能都有一个 前提条件,可以在调用之前检查 。检查那些。如果不满足,则不要拨打电话;回到该代码中的错误处理。对于那些你无法检查的功能,以确保满足前提条件......不多,该程序可能会中止。
您可以查看 避免引发异常的库 - 您在标准库的上下文中询问了这一点,因此这不太合适,但它仍然是一种选择。
其他可能的策略;我知道这听起来很陈旧,但选择一种不使用它们的语言。 C可以做得很好......
...问题的关键(你与标准库的交互,如果有的话),我很想听听你的构造函数。它们是否会失败,或者按照惯例使用具有专用init函数的两步构造,该函数可以在失败时返回错误代码(构造函数不能)?或者你的战略是什么?
如果使用构造函数,通常有两种方法用于指示失败;
enum
以指示失败以及失败是什么。在对象构造和采取适当的措施后,可以对此进行查询。init()
方法来构造(或完成)构造。如果出现故障,成员方法可以返回错误。 init()
技术的使用通常受到青睐,因为它可以链接并且比内部“错误”代码更好地扩展。
同样,这些技术来自不存在异常的环境(例如C)。使用诸如C ++之类的语言没有例外限制了它的可用性和标准库的广度的有用性。
答案 4 :(得分:8)
我没有尝试完全回答您提出的问题,我只是将谷歌作为代码库的示例,它不会将异常作为处理错误的机制。
在Google C ++代码库中,每个可能失败的函数都会返回一个status
对象,该对象具有ok
等方法来指定被调用者的结果。
如果开发人员忽略了返回status
对象,他们已经配置了GCC以使编译失败。
另外,从它们提供的小开源代码(例如LevelDB库)来看,它们似乎并没有那么多地使用STL,因此异常处理变得罕见。正如泰特斯温特斯在CPPCon的讲座中所说,他们“尊重标准,但不要崇拜它”。
答案 5 :(得分:0)
我认为这是一个态度问题。您需要处于“我不在乎是否会失败”的阵营。 这通常会导致产生代码,对于该代码,需要调试器(在客户现场)进行查找,为什么突然有些东西不再起作用了。 同样,以这种方式进行软件“工程”的人们可能不会使用非常复杂的代码。例如。一个人将无法编写代码,这取决于只有在它依赖的所有n个资源都已成功分配(同时使用RAII进行这些资源使用)的情况下,它才被执行。 因此:这样的编码将导致:
请注意,我正在谈论现代代码,按需加载客户提供的dll,并使用子进程。有许多接口可能会发生故障。我不是说要替换grep / more / ls / find。