Is my use case for C ++ catch, exception and destruction class families reasonable?

From time to time I notice some code that I have had for many years, and it makes me nervous. I don't have a specific problem, but I also don't remember enough why I adopted this model, and some of its aspects seem to fit the anti-pattern. This recently happened to me with WRT, as some of my codes use exceptions.

Anxious case includes cases when I get an exception "by reference", treating it in the same way as I will handle a function parameter. One reason for this is that I can have an inheritance hierarchy of exception classes and specify a more general or more accurate catch type depending on the application. For example, I could determine ...

class widget_error {}; class widget_error_all_wibbly : public widget_error {}; class widget_error_all_wobbly : public widget_error {}; void wibbly_widget () { throw widget_error_all_wibbly (); } void wobbly_widget () { throw widget_error_all_wobbly (); } void call_unknown_widget (void (*p_widget) ()) { try { p_widget (); } catch (const widget_error &p_exception) { // Catches either widget_error_all_wibbly or // widget_error_all_wobbly, or a plain widget_error if that // is ever thrown by anything. } } 

Now this bothers me because I noticed that the class instance was constructed (as part of the throw) inside the function, but was referenced (via the "catch-clause" parameter of the p_Exception parameter) after this function exited. Usually this is an anti-pattern - a link or pointer to a local variable or temporary one created inside the function, but passed when the function exits, is usually a dangling reference / pointer, since the local variable / temporary is destroyed and the memory is freed when the function exits.

Some quick tests suggest that the throw above is probably OK - the instance created in the throw clause does not crash when the function completes, but it crash when the final catch-clause completes - if the catch block does not throw an exception, in which case the next the catch block does this job.

My remaining nervousness is that a test run in one or two compilers is not proof of what the standard says, and since my experience is that what I consider common sense is often different from what the language guarantees.

So - is this exception handling pattern (catching them using a reference type) safe? Or should I do something else, for example ...

  • Catching (and explicitly deleting) pointers to instances allocated by the heap, instead of references to something that looks (when thrown) is very similar to temporary?
  • Using a smart pointer class?
  • Using a catch-by-value catch clause and accepting that I cannot catch any class of exceptions from a hierarchy with a single catch clause?
  • Something I didn't think about?
+7
source share
5 answers

This is normal. It's actually good to catch exceptions with a permalink (and badly catch pointers). Capturing by value creates an unnecessary copy. The compiler is smart enough to handle the exception (and its destruction) correctly - just don't try to use the exception link outside your catch block; -)

In fact, what I often do is inherit my hierarchy from std :: runtime_error (which inherits from std :: exception). Then I can use .what() and use fewer catch blocks when handling more exceptions.

+9
source

This template is definitely safe.

There are special rules that extend the lifetime of an abandoned object. In fact, it exists as long as it is being processed, and it is guaranteed to exist until the end of the last catch that processes it.

One very common idiom, for example, to get custom exceptions from std::exception , overrides its what() member function and catches it by reference so that you can print error messages from a wide range of exceptions with one catch clause.

+6
source

No, you are definitely doing it right. See http://www.parashift.com/c++-faq-lite/exceptions.html#faq-17.13 , as well as the rest of the FAQ chapter.

+4
source

Yes. So far so good.

Personally, I use std :: runtime_error as the base for all calsses exceptions. It processes error messages, etc.

Also, do not specify any more exceptions that you need. Define an exception only for things that can be caught and fixed. Use the more general exception for things that cannot be caught or fixed.

For example: if I create library A. Then I will have an AException derived from std :: runtime_error. This exception will be used for all common library exceptions. For any special exceptions, when a library user can actually catch and do something (fix or mitigate) with an exception, I will create a specific exception, obtained from AException (but only if there is something that can be done with the exception).

+4
source

Indeed, Sutter and Alexandrescu recommend this template in their C ++ Coding Standards.

+4
source

All Articles