我听说你永远不应该抛出一个字符串,因为缺少信息,你会发现你不希望捕获的异常。抛出异常有什么好的做法?你继承了一个基本的异常类吗?你有很多例外或很少吗?你做MyExceptionClass&或const MyExceptionClass& ?此外,我知道永远不应该在析构函数中抛出异常
我将补充说,我理解按合同设计以及何时抛出异常。我问我应该如何抛出异常。
答案 0 :(得分:12)
在我看来,如果一个函数不能保持其“承诺”,如果它必须打破它的“契约”,它应该抛出异常。函数的签名(名称和参数)决定了它的合同。
鉴于这两个成员函数:
const Apple* FindApple(const wchar_t* name) const;
const Apple& GetApple(const wchar_t* name) const;
这些函数的名称及其返回值向我表明,在 FindApple 的情况下,当找不到正确的apple时,该函数完全能够返回NULL,但在这种情况下 GetApple 你期待苹果回归。如果第二个函数不能保证其承诺,它必须抛出异常。
例外是指那些功能无法以其他方式报告这些条件的特殊情况。如果您决定将其作为承诺的一部分(读取:函数签名),那么它可以报告该条件而不会抛出异常。
请注意,在 FindApple 的情况下,由调用者决定如何处理“找不到合适的苹果”的情况,因为它不再是一种特殊情况。
您可能会试图避免所有异常,但这意味着您必须考虑所有可能的异常情况,并且您将负担放在调用方上。调用者需要检查“错误条件”。
最终,需要处理异常,但只有呼叫者知道如何以有用的方式处理特定条件。我的意思是最广泛的解释:放弃的服务将在稍后再试,提供有用的错误消息的UI,提供“oops”屏幕的网络应用程序,但恢复得很好,......等等
戴夫
答案 1 :(得分:9)
一个基本的事情是仅为特殊情况保留例外。不要将它们用于流量控制。例如,“找不到文件”不应该是例外,它应该是错误代码或返回值(除非文件是必须存在的东西,例如配置文件)。但是如果文件在处理时突然消失,那么抛出异常是一个不错的选择。
当谨慎使用异常时,您不需要将代码转换为try-catch -spaghetti,以避免从较深层接收难以理解的上下文异常。
答案 2 :(得分:4)
使用标准例外!如果您有特定错误,请尝试使用返回值来避免它。如果您 要使用例外,请定义从Exception继承并创建自定义消息的自定义异常。
答案 3 :(得分:3)
有时您可能无法返回错误代码,例如。当你需要出现错误情况时的确切背景,例如。当你需要传播错误状态3级时 - 你松散了上下文。
在这种情况下,自定义类是最佳解决方案。我使用这种方法,定义我自己的内联类(它们没有.cpp;只有.h)例如:
class DeviceException {
;
}
class DeviceIOException: public DeviceException {
DeviceIOException(std::string msg, int errorCode);
}
等
然后,我可以按类型和包含在其中的信息来判断/处理异常。答案 4 :(得分:1)
我总是抛出一个异常,并附上一个消息,说明它发生的原因以及导致它发生的原因:
throw NException("Foo::Bar", "Mungulator cause a stack overflow!");
然后,您可以在消息框等中使用这些字符串。
我总是通过
抓住catch (NException& ex) { ... }
如果您运行Windows,则可以传递错误值并使函数派生错误消息。最好的例子是Windows via C/C++ by Jeffrey Richter。
答案 5 :(得分:1)
投掷指针可能不是一件好事,因为它使抛出对象的所有权变得复杂。类型类型异常可能比基本因素更好,因为它们可以包含有关异常原因的更多信息。
在使用类或类层次结构时,您应该考虑以下几点:
复制构造函数和 异常对象的析构函数 绝不能抛出异常。如果 他们你是程序会的 立即终止。(ISO 15.5 / 1)
如果你的异常对象有基础 类,然后使用公共继承 仅为a选择处理程序 如果基数派生到基类 class是可访问的。(ISO 15.3 / 3)
最后,(对于所有例外类型)确保 被抛出的表达式不能 本身导致抛出异常。
例如:
class Ex {
public:
Ex(int i)
: m_i (i)
{
if (i > 10) {
throw "Exception value out of range";
}
}
int m_i;
};
void foo (bool b) {
if (! b) {
// 'b' is false this is bad - throw an exception
throw Ex(20); // Ooops - throw's a string, not an Ex
}
}
答案 6 :(得分:1)
你应该总是抛出一个派生自std :: exception的异常类。这允许您的界面具有一定的一致性,并允许这些方法或功能的客户端具有更大的灵活性。例如,如果要添加catch all处理程序,则可以添加
catch(std::exception& e)块并完成它。 (尽管如果你不控制所有可以抛出的代码,你常常无法逃脱。)
我倾向于只抛出标准提供的异常(即std :: runtime_error),但是如果你想为处理程序提供额外的粒度,你可以随意从std :: exception派生自己的。请参阅the C++ FAQ lite。
此外,您应该抛出一个临时的并通过引用捕获它(以避免在您的catch站点调用copy ctor)。投掷指针也不赞成,因为不清楚谁应该清理记忆。 C++ FAQ Lite也处理此事。
答案 7 :(得分:0)
对于当前项目,我们考虑了主程序循环可以采取的适当措施。基本程序接受XML消息,并将信息保存到数据库中(中间有大量处理)。
第一项是检查异常,因为我们认为数据检查是方法接口的一部分。其他的是未经检查的,因为主循环不能知道子组件的实现,例如,实现可以使用SQL数据库,也可以简单地将数据保存在内存中 - 调用者不需要知道。
答案 8 :(得分:0)
正如之前所说,仅在特殊情况下使用它们。
始终为用户提供避免抛出异常的方法,例如。如果你有方法,那么如果出现问题就会抛出:
public void DoSomethingWithFile() {
if(!File.Exists(..))
throw new FileNotFoundException();
}
为用户提供另一种方法:
public bool CanDoSomething() {
return File.Exists(..);
}
这样调用者可以在需要时避免异常。 如果出现问题,请不要犹豫 - “快速失败”,但始终提供无异常的路径。
同时保持您的异常类层次结构平坦,并查看标准异常,如InvalidStateException和ArgumentNullExcpetion。
答案 9 :(得分:0)
如果您正在从其他开发人员将使用下游的组件中抛出异常,请大家帮忙,并始终从std :: exception派生您自己的异常类(如果您确实需要使用标准异常的c.f)。不惜一切代价避免全面侮辱,例如投掷注册,HRESULTS,char *,std :: string ......
答案 10 :(得分:0)
以下是抛出几乎不占用任何资源的异常的简单示例:
class DivisionError {};
class Division
{
public:
float Divide(float x, float y) throw(DivisionError)
{
float result = 0;
if(y != 0)
result = x/y;
else
throw DivisionError();
return result;
}
};
int main()
{
Division d;
try
{
d.Divide(10,0);
}
catch(DivisionError)
{
/*...error handling...*/
}
}
抛出的空类不占用任何资源或很少...
答案 11 :(得分:0)
从C ++常见问题解答[17.12] What should I throw?:
通常,最好抛出对象,而不是内置函数。 如果可能,您应该抛出类的实例 派生(最终)来自
std::exception
类。
...和
最常见的做法是暂时抛出: (参见下面的示例)