如何抛出好的异常?

发布于 2024-07-13 22:35:21 字数 223 浏览 11 评论 0原文

我听说你永远不应该抛出字符串,因为缺乏信息,并且你会捕获你不希望捕获的异常。 抛出异常的良好做法是什么? 你继承了异常基类吗? 你有很多例外还是很少例外? 你做MyExceptionClass&吗? 或 const MyExceptionClass& ? 另外,我知道异常不应该在析构函数中抛出,

我将补充说,我了解合同设计以及何时抛出异常。 我问我应该如何抛出异常。

I heard you should never throw a string because there is a lack of information and you'll catch exceptions you dont expect to catch. What are good practice for throwing exceptions? do you inherit a base exception class? Do you have many exceptions or few? do you do MyExceptionClass& or const MyExceptionClass& ? etc. Also i know exceptions should never been thrown in destructors

i'll add that i understand design by contract and when to throw exception. I am asking how i should throw exceptions.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(12

风铃鹿 2024-07-20 22:35:21

在我看来,如果一个函数不能遵守其“承诺”,或者必须打破其“合同”,则它应该抛出异常。 函数的签名(名称和参数)决定了它的契约。

给定这两个成员函数:

const Apple* FindApple(const wchar_t* name) const;
const Apple& GetApple(const wchar_t* name) const;

这些函数的名称及其返回值向我表明,在 FindApple 的情况下,当未找到正确的苹果时,该函数完全能够返回 NULL,但是对于GetApple,您期望返回一个苹果。 如果第二个函数不能兑现其承诺,它必须抛出异常。

异常是指功能没有其他方式报告这些情况的异常情况。 如果您决定将其作为承诺的一部分(阅读:函数签名),那么它可以报告该条件而不引发异常。

请注意,在 FindApple 的情况下,由调用者决定如何处理“未找到正确的苹果”的情况,因为它不再是异常情况。

您可能会试图避免所有异常,但这意味着您必须考虑所有可能的异常情况,并且您将负担转嫁给了调用者。 然后调用者需要检查“错误条件”。

最终,需要处理异常,但只能由知道如何以有用的方式处理特定情况的调用者来处理。 我的意思是在最广泛的可能解释中:放弃的服务稍后会再次尝试,提供有用的错误消息的 UI,呈现“哎呀”屏幕但恢复良好的 Web 应用程序,......等等。

戴夫

In my opinion, a function should throw an exception if it can't keep its "promise", if it has to break its "contract". The function's signature (name and parameters) determine its contract.

Given these two member functions:

const Apple* FindApple(const wchar_t* name) const;
const Apple& GetApple(const wchar_t* name) const;

The names of these functions as well as their return values indicate to me that in the case of FindApple the function is perfectly capable of returning NULL when the correct apple was not found, but in the case of GetApple you're expecting an apple to return. If that second function can't keep its promise, it must throw an exception.

Exceptions are meant for those exceptional conditions in which a function has no other way of reporting these conditions. If you decide to make it a part of the promise (read: function signature) then it can report that condition without throwing an exception.

Note that in the case of FindApple, it's up to the caller to decide how to handle the condition of "not finding the right apple" because it's no longer an exceptional condition.

You might be tempted to try to avoid all exceptions, but that means you have to account for all possible exceptional conditions, and you're placing the burden on the caller instead. The caller needs to check for "error conditions" then.

Ultimately, an exception needs to be handled, but only by the caller that knows how to handle a particular condition in a useful way. And I mean this in the widest possible interpretation: a service that gives up will try again later, a UI that provides a helpful error message, a web app that presents a "oops" screen but that recovers nicely, ... and so on.

Dave

高跟鞋的旋律 2024-07-20 22:35:21

一件基本的事情是只为特殊情况保留例外。 不要将它们用于流量控制。 例如,“文件未找到”不应该是异常,它应该是错误代码或返回值(除非该文件必须存在,例如配置文件)。 但是,如果文件在处理时突然消失,那么抛出异常是一个不错的选择。

当谨慎使用异常时,您不需要将代码变成 try-catch 意大利面条式的代码,以避免从更深的层接收到无法理解的上下文异常。

One basic thing is to reserve exceptions for exceptional situations only. Don't use them for flow control. For instance, "file not found" should not be an exception, it should be an error code or return value (unless the file is something that must exist, e.g. a configuration file). But if a file suddenly disappears while you're processing it, then throwing an exception is a good choice.

When exceptions are used sparingly, you don't need to turn your code into a try-catch -spaghetti in order to avoid receiving incomprehensible-in-the-context exceptions from the deeper layers.

一个人的旅程 2024-07-20 22:35:21

使用标准异常! 如果出现特定错误,请尝试使用返回值来避免它。 如果您必须使用异常,请定义继承自 Exception 的自定义异常并创建自定义消息。

Use the standard exceptions! If you have a specific error, try to avoid it with return value. If you have to use exceptions, define your custom exception that inherits from Exception and create a custom message.

坦然微笑 2024-07-20 22:35:21

有时可能会发生您无法返回错误代码的情况,例如。 当您需要错误情况发生时的确切上下文时,例如。 当您需要将错误状态向上传播 3 级时 - 您会失去上下文。

在这种情况下,自定义类是最好的解决方案。 我使用这种方法,定义我自己的内联类(它们没有 .cpp;只有 .h),例如:

class DeviceException {
    ;
}

class DeviceIOException: public DeviceException {
    DeviceIOException(std::string msg, int errorCode);
}

等等。

然后我可以通过类型和其中包含的信息来判断/处理异常。

Sometimes it can happen that you're not able to return error code eg. when you need exact context of when error situation occured, eg. when you need to propagate error status 3 levels up - you loose context.

In this situation custom class is the best solution. I use this approach, defining my own inline classes (there's no .cpp for them; only .h) eg.:

class DeviceException {
    ;
}

class DeviceIOException: public DeviceException {
    DeviceIOException(std::string msg, int errorCode);
}

etc.

I then can judge/act upon the exception by type and by information contained within.

书间行客 2024-07-20 22:35:21

我总是抛出一个异常,并带有一条消息,说明异常发生的位置以及导致异常发生的原因:

throw NException("Foo::Bar", "Mungulator cause a stack overflow!");

然后您可以在消息框中使用这些字符串等。

我总是通过捕获

catch (NException& ex) { ... }

如果您运行 Windows,则可以传递错误值并让函数派生错误信息。 最好的例子是 Jeffrey Richter 通过 C/C++ 编写的 Windows

I always throw an exception with a message of where it occurred and what caused it to happen:

throw NException("Foo::Bar", "Mungulator cause a stack overflow!");

You can then use these strings in messageboxes etc.

I always catch via

catch (NException& ex) { ... }

If you running windows you can pass the error value and have a function derive the error message. The best example of this is in Windows via C/C++ by Jeffrey Richter.

情深已缘浅 2024-07-20 22:35:21

抛出指针可能不是一件好事,因为它使抛出对象的所有权变得复杂。 类类型异常可能比基础类型更好,因为它们可以包含有关异常原因的更多信息。

在使用类或类层次结构时,您应该考虑以下几点:

  1. 复制构造函数和
    异常对象的析构函数
    绝不能抛出异常。 如果
    他们会做你的计划
    立即终止。(ISO 15.5/1)

  2. 如果你的异常对象有基础
    类,然后使用公共继承。
    处理程序只会被选择用于
    派生到基类,如果基类
    可访问。(ISO 15.3/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
  }
}

Throwing pointers is probably not a good thing, as it complicates ownership of the thrown object. Class type exceptions are probably better than fundamentals simply because they can contain more information about the reason for the exception.

In using a class or class hierarchy there are a couple of points you should consider:

  1. Both the copy constructor and
    destructor of the exception object
    must never throw an exception. If
    they do you're program will
    terminate immediately.(ISO 15.5/1)

  2. If your exception objects have base
    classes, then use public inheritance.
    A handler will only be selected for a
    derived to base class if the base
    class is accessible.(ISO 15.3/3)

  3. Finally, (for all exception types) ensure
    that the expression being thrown cannot
    itself result in an exception being thrown.

For example:

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
  }
}
纵性 2024-07-20 22:35:21

您应该始终抛出一个从 std::exception 派生的异常类。 这允许您的界面具有一定的一致性,并为这些方法或函数的客户端提供更大的灵活性。 例如,如果您想添加一个捕获所有处理程序,您可以添加一个

catch(std::exception& e)

block and be done with it. (Though often you won't be able to get away with that if you don't control all the code that can throw).

我倾向于仅抛出标准提供的异常(即 std::runtime_error),但如果您想为处理程序提供额外的粒度,您应该随意从 std::exception 派生自己的异常。 请参阅C++ 常见问题解答精简版

另外,您应该抛出一个临时变量并通过引用捕获它(以避免在您的捕获站点调用复制构造函数)。 抛出指针也是不受欢迎的,因为不清楚谁应该清理内存。 C++ FAQ Lite 也处理这个问题。

You should always throw an exception class derived from std::exception. This allows a certain consistency to your interface and allows more flexibility to the clients of these methods or functions. For example if you want to add a catch all handler you may be able to add a

catch(std::exception& e)

block and be done with it. (Though often you won't be able to get away with that if you don't control all the code that can throw).

I tend to throw only exceptions provided by the the standard (i.e. std::runtime_error) but if you want to provide extra granularity to your handlers, you should feel free to derive your own from std::exception. See the C++ FAQ lite.

Also, you should throw a temporary and catch it by reference (to avoid the copy ctor be invoked at your catch site). Throwing pointers is also frowned upon since it is unclear who should clean up the memory. C++ FAQ Lite deals with this too.

柠檬 2024-07-20 22:35:21

对于当前的项目,我们考虑了主程序循环可以采取的适当操作。 基本程序接受 XML 消息,并将信息保存到数据库中(其间进行大量处理)。

  1. 数据错误表明输入有问题。 适当的操作是将消息保存到日志目录但不处理它。
  2. 基础设施错误表明某些子组件(如输入队列、SQL 数据库、JNI 库)出现故障。 睡眠几分钟然后重新连接。
  3. 配置错误表明某些方面配置无法工作。 退出程序。

第一项是检查异常,因为我们认为数据检查是方法接口的一部分。 其他的未被检查,因为主循环无法知道子组件的实现,例如,实现可能使用 SQL 数据库,或者可能只是将数据保存在内存中——调用者不需要知道。

For a current project, we thought about the appropriate action that could be taken by the main program loop. The basic program accepts XML messages, and saves the information into a database (with a fair amount of processing in between).

  1. Data errors that indicate something wrong the input. Appropriate action is to save the message to a log directory but not process it.
  2. Infrastructure errors that indicate some subcomponent (like the input queue, an SQL database, a JNI library) is malfunctioning. Sleep for a few minutes then reconnect.
  3. Configuration errors that indicate some aspect configuration is unworkable. Exit the program.

The first item is a checked exception, since we considered data checking to be part of a method's interface. The others are unchecked since the main loop cannot know the implementations of subcomponents, e.g. an implementation may use an SQL database, or may simply persist data in memory -- the caller doesn't need to know.

一笔一画续写前缘 2024-07-20 22:35:21

如果您从其他开发人员将在下游使用的组件抛出异常,请帮他们一个大忙,并始终从 std::exception 派生您自己的异常类(如果您确实需要它们,请使用标准异常)。 不惜一切代价避免完全令人厌恶的行为,例如抛出 ints、HRESULTS、char*、std::string...

If you're throwing exceptions from a component other developers will be using downstream, please do them a big favour and always derive your own exception classes (if you really need them c.f using the standard exceptions) from std::exception. Avoid at all costs utter abominations like throwing ints, HRESULTS, char*, std::string...

行至春深 2024-07-20 22:35:21

正如已经说过的,仅在特殊情况下使用它们。

始终为用户提供一种避免引发异常的方法,例如。 如果你有方法,如果出现如下错误,就会抛出:

public void DoSomethingWithFile() {
    if(!File.Exists(..))
        throw new FileNotFoundException();
}

提供另一种方法供用户调用:

public bool CanDoSomething() {
    return File.Exists(..);
}

这样调用者可以根据需要避免异常。
如果出现问题,请毫不犹豫地抛出 - “快速失败”,但始终提供无异常路径。

还要保持异常类层次结构平坦,并查看标准异常,例如 InvalidStateException 和 ArgumentNullExcpetion。

As it has been already said use them for exceptional situations only.

Always provide a way for the user to avoid throwing an exception, eg. if you have method, that will throw if something goes wrong like this:

public void DoSomethingWithFile() {
    if(!File.Exists(..))
        throw new FileNotFoundException();
}

Provide another method for the user to call:

public bool CanDoSomething() {
    return File.Exists(..);
}

This way there the caller can avoid exceptions if he wants.
Do not hesitate to throw if something is wrong - "fail fast", but always provide exception-free path.

Also keep your exception class hierarchy flat and take a look at the standard exceptions like InvalidStateException and ArgumentNullExcpetion.

╰◇生如夏花灿烂 2024-07-20 22:35:21

这是一个抛出几乎不需要任何资源的异常的简单示例:

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...*/
    }
}

抛出的空类不占用任何资源或很少......

Here is a simple example of throwing an exception that takes barely any resources:

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...*/
    }
}

The empty class that is thrown does not take any resource or very few...

别把无礼当个性 2024-07-20 22:35:21

来自 C++ 常见问题解答,[17.12] 我应该抛出什么?:

一般来说,最好抛出对象,而不是内置对象。
如果可能的话,您应该抛出以下类的实例
(最终)从 std::exception 类派生。

...和

最常见的做法是抛出一个临时的:
(参见下面的示例)

From the C++ FAQ, [17.12] What should I throw?:

Generally, it's best to throw objects, not built-ins.
If possible, you should throw instances of classes that
derive (ultimately) from the std::exception class.

...and

The most common practice is to throw a temporary:
(see example that follows)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文