题
我听说你应该永远不会扔一串因为缺乏信息而且你会赶上的例外,你不要期望。什么是好的做法,为投掷例外?你继承的异常基类?你有许多例外情况或几个?你做MyExceptionClass&或const MyExceptionClass&?等等。我也知道的例外情况永远不应该被扔在析构
我要补充一点,我理解的设计的合同和当扔例外。我问我应该怎么扔例外情况。
解决方案
在我看来,一个函数应该抛出一个异常,如果它不能保持它的“承诺”,如果它打破它的“合同”。函数的签名(名称和参数)确定其合同。
给定这两个部件的功能:
const Apple* FindApple(const wchar_t* name) const;
const Apple& GetApple(const wchar_t* name) const;
这些功能以及它们的返回值指示我的名字,在的情况下的 FindApple 的功能是完全有能力时,没有找到正确的苹果返回NULL,但在案件的 GetApple 你希望苹果返回。如果第二功能不能信守承诺,就必须抛出异常。
异常是指那些异常条件,其中一个函数具有报告这些条件的任何其他方式。如果您决定使其成为一个有前途的部分(读:函数签名)。然后,它可以在不引发异常报告条件
请注意,在的情况下的 FindApple ,它是由主叫方来决定如何处理“没有找到合适的苹果”,因为它不再是一个特殊的条件的情况。
您可能想尽量避免所有的异常,但是这意味着你必须考虑到所有可能的异常情况,并要拨打的负担,而不是调用者。呼叫者需要检查“错误条件”,则
最终,一个异常需要处理,但是仅通过的呼叫者知道如何以一种有用的方式处理特定的条件即可。我的意思是这最广泛可能的解释:一种服务,放弃以后将再次尝试,提供了一个有用的错误消息的UI,一个Web应用程序,提出了“哎呀”筛网但恢复很好,...等等
戴夫
其他提示
一个根本的是要保留的只有特殊情况例外。不要将它们用于流量控制。例如,“未找到文件”不应该是一个例外,它应该是一个错误码或返回值(除非文件是东西必须存在,例如配置文件)。但是,如果一个文件,当你处理它,然后抛出一个异常突然消失是一个不错的选择。
在异常谨慎使用,你不需要把你的代码放到一个try-catch -spaghetti,以避免从更深的层次接收难以理解合的上下文例外。
使用标准的例外!如果你有一个特定的错误,请尝试使用返回值,以避免它。如果您的,以使用异常,定义从Exception继承您的自定义异常,并创建自定义消息。
有时,它可能发生,你不能够返回错误代码如。当你需要时,错误情况发生的确切情况下,例如。当你需要传播错误状态3个水平高达 - 你松散上下文
在这种情况下,自定义类是最好的解决办法。我用这个方法,定义我自己的内嵌类(有没有的.cpp他们;只有.H),例如:
class DeviceException {
;
}
class DeviceIOException: public DeviceException {
DeviceIOException(std::string msg, int errorCode);
}
等
我然后可以按类型和包含在信息判断/异常时动作。
我总是扔一个异常的信息在那里发生什么导致它发生这样的情况:
throw NException("Foo::Bar", "Mungulator cause a stack overflow!");
然后可以使用这些字符串中的消息框等。
我总是追赶通过
catch (NException& ex) { ... }
如果你的窗户可以通过误差值时,并有一个功能得出错误信息。最好的例子是在 Windows通过C/C++由杰弗里*里希特.
抛出指针可能不是一件好事,因为它使抛出对象的所有权变得复杂。类类型异常可能比基础类型更好,因为它们可以包含有关异常原因的更多信息。
在使用类或类层次结构时,您应该考虑以下几点:
异常对象的复制构造函数和破坏者都绝不能抛出异常。如果他们这样做,您的程序将立即终止。(ISO 15.5/1)
如果您的异常对象具有基类,请使用公共继承。
如果基类是 无障碍.(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
}
}
您应该总是抛出从标准::异常派生的异常类。这允许你的界面有一定的一致性,并允许更多的灵活性,这些方法或功能的客户端。例如,如果你想添加一个catch你可以添加
catch(std::exception& e)块和所有的处理程序用它做。 (尽管很多时候你将无法逃脱,如果你不控制所有可能抛出的代码)。
我倾向于扔仅由标准(即的std :: runtime_error)提供例外,但如果你想提供额外的粒度的处理程序,你应该感到自由利用的std ::异常派生自己。请参阅的C ++ FAQ精简版。
此外,你应该抛出一个临时的,并通过引用捕捉它(避免拷贝构造函数在你抓的网站被调用)。扔三分球也令人难以接受的,因为它目前还不清楚谁应该清理内存。 C ++ FAQ精简版涉及这一点。
对于当前的项目,我们考虑了主程序循环可以采取的适当操作。基本程序接受 XML 消息,并将信息保存到数据库中(其间进行大量处理)。
- 数据错误表明输入有问题。适当的操作是将消息保存到日志目录但不处理它。
- 基础设施错误表明某些子组件(如输入队列、SQL 数据库、JNI 库)出现故障。睡眠几分钟然后重新连接。
- 配置错误表明某些方面配置无法工作。退出程序。
第一项是检查异常,因为我们认为数据检查是方法接口的一部分。其他未检查,因为主循环无法知道子组件的实现,例如实现可以使用 SQL 数据库,或者可以简单地将数据保留在内存中——调用者不需要知道。
如果你从一个组件其他开发人员将使用下游抛出异常,请做他们一个大忙,总是得出自己的异常类(如果你真的需要他们c.f使用标准的例外)从的std ::例外。不惜一切代价避免像扔整数彻底的憎恶,HRESULTS,字符*,的std :: string ...
因为已经说过将它们用于特殊情况下仅
始终为用户提供,以避免抛出异常,例如一种方式。如果你有方法,将抛出,如果出现错误是这样的:
public void DoSomethingWithFile() {
if(!File.Exists(..))
throw new FileNotFoundException();
}
提供的另一种方法为用户呼叫:
public bool CanDoSomething() {
return File.Exists(..);
}
有这样主叫方可避免例外,如果他想。 不要犹豫,扔,如果事情是错的 - “快速失败”,但总是免费提供异常路径
另外,请您异常类层次结构平坦,看看标准的例外像InvalidStateException和ArgumentNullExcpetion。
下面是抛出异常发生几乎没有任何资源的一个简单的例子:
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...*/
}
}
时引发的空类不采取任何资源或很少...
从C ++ FAQ, [17.12]我应该丢?:
一般情况下,这是最好的抛出对象,而不是内置插件。 如果可能的话,你应该抛出的类的实例是 派生(最终)从
std::exception
类。
...和
最常见的做法是抛出一个临时: (参见实施例下面)