C++
允许 throw
任何类型的对象,从 exception
到 string
甚至 int
。
但我从未见过除了 exception
之外其他类型对象的真实应用场景中使用 throw
。
我的问题是,throw
非 exception
对象的应用场景是什么?
从实用角度来看,几乎没有任何应用程序可以抛出 string
、int
或其他非源自 std::exception
的内容。
这不是因为没有指示这样做,而是因为有反指示表明你不应该这样做。
有两个主要原因,为什么你不想抛出任何不源自 std::exception
的东西:
std::string
,并且该 string
的构造或复制引发另一个异常,那么将调用 terminate
,你的进程将停止运行。你再也没有机会捕获那个std::string
。std::exception
的异常,使得能够以通用方式catch (const std::exception&)
。如果你抛出其他东西,你需要为该情况编写一个catch
。这里有一个关于异常的好讨论。
1 几乎没有任何应用程序可以抛出......: 每个规则都有例外,但即使承认这一点,我从未见过一个合法的抛出非 std::exception
派生类的情况。
throw std::string("oops")
这样的语句不会导致程序终止。如果构造函数抛出异常,那么 throw-expression 就不会被评估,因此没有第二个 throw,就像 throw f(std::string("oops"))
不会调用 f
一样。 - aschepler这更像是一种黑客技巧而非语言特性,你可以抛出一个对象然后捕获它,以强制函数“返回”与其正常返回类型不同的内容。
int aFunc()
{
throw foo(); // if you catch that foo, you 've imitated returning foo
return 0; // ok just an int
}
throw 1;
能够与 catch(std::exception)
配合使用吗?因为 "You throw whatever you wants, it will always be an exception as you throw it." 这句话的意思是无论你抛出什么,它都会被视为异常。 - Luchian Grigorestd::exception
派生出来的东西,几乎没有什么好的理由可以抛出其他东西。 - John Diblingstd::exception
。你假设我做出了一个我没有做出的陈述。 - Luchian Grigore我不确定是什么促使了这个问题,因为在这种情况下,你可以自由选择“应用程序”。
对象异常的目的是指示异常发生的事实,并通常携带一些特定于异常的信息从抛出者到处理者。如果某种类型足以满足您的应用程序的要求,则可以将该类型用作“异常”类型。您可以抛出int
值、std::string
值和任何其他值,如果您愿意。完全取决于您。
如果您想从抛出者传递给处理者的只是一个int
值,则类型int
将作为“异常”类型服务。就是这样。
C++不对您可以抛出的类型施加任何特定要求。因此,真正没有所谓的“异常”类型或“非异常”类型。什么是“异常类型”和什么不是由您在代码中决定。只要您知道如何捕获它并解释您捕获的内容,您可以抛出任何东西。
使用类类型来表示异常的第一个好处是类类型是自由可定义的,即您可以轻松定义任意数量的异常类型。然后,您可以使用许多独立、不干扰的异常“流”,它们仅抛出和捕获其各自类型的异常。int
的值来表示您的异常。这将起作用。但我几乎可以肯定,您很快就会遇到这种方法在维护和扩展性方面的限制。std::exception
派生对象之外的其他东西,而是何时应该抛出。至少我是这样理解这个问题的。如果你断言有合法的情况可以抛出不派生自exception
的东西,你能详细说明一下吗?说实话,“只要你知道你在做什么就可以”听起来有点模糊。 - John Diblingstd::exception
。当然也有例外情况,最明显的是:我曾经参与过的项目更倾向于抛出一个int
而不是调用exit()
。当然,这只在main
遵循捕获int
并返回它的约定时才起作用。(这样做的优点是,所有局部变量都将被析构,而如果你调用exit()
,则不会。)char const*
捕获),这样可以立即输出。(这不是任何生产代码的好方法,但当你为了学习而进行试验时,这似乎是可以接受的。)正如 @galop1n 所回答的,你可以抛出任何你喜欢的东西。但是
throw(type())
---> catch(type const& e)
catch(std::exception const& e)
捕获所有可记录日志的东西的期望。尽管你可能会反驳说,他们不应该捕获任何他们不知道是什么的东西。我不同意一般观点认为只能抛出 std::exception 的派生类。当然,我可以认同抛出具有复杂数据或行为的对象可能非常棘手,应该谨慎行事。
然而,这并不意味着抛出类型的继承层次结构受到任何限制,也不意味着您的应用程序使用堆栈展开的方式(或原因)受到任何限制。而 throw 操作仅仅是这样一个干净而明确定义的方法:在达到定义点(匹配的 catch)之前卸载堆栈,而无需涉及中间帧中的显式处理程序。
当然,有合法的抛出用例,并不意味着发生错误或任何异常情况。例如,考虑一个简单的语言解释器,其中主机语言的堆栈对应于客户语言的堆栈。为了在客户语言中实现交错并行性,当客户操作阻塞(或产生值)时,堆栈展开正是您需要执行的操作。您想卸载到并行性的起始点,并尝试并行分支中的下一个操作。这种情况既不是异常情况,也不是任何类型的错误。
请注意,关键字被称为throw
/catch
,而exception
概念并未以任何方式嵌入语言中。这是其中之一的原因。
std::exception
派生的异常中可能是更好的想法,而不是直接捕获枚举错误码或类似的。 - πάντα ῥεῖ