throwing exceptions out of a destructor

后端 未结 16 1869
暗喜
暗喜 2020-11-22 00:23

Most people say never throw an exception out of a destructor - doing so results in undefined behavior. Stroustrup makes the point that \"the vector destructor e

相关标签:
16条回答
  • 2020-11-22 01:16

    Throwing out of a destructor can result in a crash, because this destructor might be called as part of "Stack unwinding". Stack unwinding is a procedure which takes place when an exception is thrown. In this procedure, all the objects that were pushed into the stack since the "try" and until the exception was thrown, will be terminated -> their destructors will be called. And during this procedure, another exception throw is not allowed, because it's not possible to handle two exceptions at a time, thus, this will provoke a call to abort(), the program will crash and the control will return to the OS.

    0 讨论(0)
  • 2020-11-22 01:16

    The real question to ask yourself about throwing from a destructor is "What can the caller do with this?" Is there actually anything useful you can do with the exception, that would offset the dangers created by throwing from a destructor?

    If I destroy a Foo object, and the Foo destructor tosses out an exception, what I can reasonably do with it? I can log it, or I can ignore it. That's all. I can't "fix" it, because the Foo object is already gone. Best case, I log the exception and continue as if nothing happened (or terminate the program). Is that really worth potentially causing undefined behavior by throwing from a destructor?

    0 讨论(0)
  • 2020-11-22 01:18

    Everyone else has explained why throwing destructors are terrible... what can you do about it? If you're doing an operation that may fail, create a separate public method that performs cleanup and can throw arbitrary exceptions. In most cases, users will ignore that. If users want to monitor the success/failure of the cleanup, they can simply call the explicit cleanup routine.

    For example:

    class TempFile {
    public:
        TempFile(); // throws if the file couldn't be created
        ~TempFile() throw(); // does nothing if close() was already called; never throws
        void close(); // throws if the file couldn't be deleted (e.g. file is open by another process)
        // the rest of the class omitted...
    };
    
    0 讨论(0)
  • 2020-11-22 01:21

    I am in the group that considers that the "scoped guard" pattern throwing in the destructor is useful in many situations - particularly for unit tests. However, be aware that in C++11, throwing in a destructor results in a call to std::terminate since destructors are implicitly annotated with noexcept.

    Andrzej Krzemieński has a great post on the topic of destructors that throw:

    • https://akrzemi1.wordpress.com/2011/09/21/destructors-that-throw/

    He points out that C++11 has a mechanism to override the default noexcept for destructors:

    In C++11, a destructor is implicitly specified as noexcept. Even if you add no specification and define your destructor like this:

      class MyType {
            public: ~MyType() { throw Exception(); }            // ...
      };
    

    The compiler will still invisibly add specification noexcept to your destructor. And this means that the moment your destructor throws an exception, std::terminate will be called, even if there was no double-exception situation. If you are really determined to allow your destructors to throw, you will have to specify this explicitly; you have three options:

    • Explicitly specify your destructor as noexcept(false),
    • Inherit your class from another one that already specifies its destructor as noexcept(false).
    • Put a non-static data member in your class that already specifies its destructor as noexcept(false).

    Finally, if you do decide to throw in the destructor, you should always be aware of the risk of a double-exception (throwing while the stack is being unwind because of an exception). This would cause a call to std::terminate and it is rarely what you want. To avoid this behaviour, you can simply check if there is already an exception before throwing a new one using std::uncaught_exception().

    0 讨论(0)
提交回复
热议问题