1

I've tried to read up on the difference between return EXIT_SUCCESS; from main() and calling exit(EXIT_SUCCESS) from anywhere, and the best resource I've found so far is this answer here on SO. However, there is one detail I'd like to have cleared up.

To me, the most compelling argument against exit() (as laid forward in that post) is that no destructor is called on locally scoped objects. But what does this mean to other objects? What if I'm calling exit() from somewhere else, quite far away on the stack from the main() method, but in block (even a method) that contains only that call, and no variables? Will objects elsewhere on the stack still be destructed?

My use case is this:

I have an application that keeps prompting the user for input until the "quit" command is given (a text-based adventure game). The easiest way to accomplish that, was to map "quit" to a method that simply calls exit(EXIT_SUCCESS). Of course, I could write it so that every action the user can take returns a boolean indicating wether the game should go on or not, and then just return false when I want to quit - but the only time I'd return anything but true is from this method - every other action method would then have to return true just because I wanted to avoid exit(). On the other hand, I create quite a lot of objects and allocate quite a lot of memory dynamically - all of that has to be taken care of by class destructors, so it is crucial that they do run.

What is best practice here? Is this a good case for exit(), or just as bad as in the main method?

Community
  • 1
  • 1
Tomas Aschan
  • 58,548
  • 56
  • 243
  • 402

3 Answers3

3
if (command == "quit") {
    throw QuitGameException();
}

You could throw an exception. An exception would safely unwind the stack and destroy objects in all the callers along the way.

John Kugelman
  • 349,597
  • 67
  • 533
  • 578
  • 1
    That sounds like using Exceptions for programm flow ... exceptions are for eceptions from the normal flow, and a user requesting to leave, while being asked sounds quite normal ... – johannes Jan 08 '13 at 21:47
  • 2
    Exceptions should only be used in, well, exceptional situations. A user wanting to quit your program is not an exceptional situation. – Jehjoa Jan 08 '13 at 21:47
  • 2
    @Jehjoa, unless your program is a game and is really really really fun and you just can't stop playing it! – gustaf r Jan 08 '13 at 21:56
  • +1 This is my #2 preferred way in C++ after calling `_exit()` :—) A lot better then checking flags all around your program. Of course, given that zero-cost exception handling is available on a target architecture and this exception is thrown once during a program life-time. –  Jan 08 '13 at 22:00
  • This solution can be dangerous. If you later on want to add something that should be executed when the program is going to close (for example you might want to save a file), it might be hard to do it. – Zyx 2000 Jan 08 '13 at 22:12
  • 2
    @Zyx2000 not really, you do it with RAII in destructors. Vlad, if the exception is thrown once, why'd you care about zero-cost? And, zero-cost exception handling doesn't exist in real life. It's very expensive, if we're talking about real exceptions. – gustaf r Jan 08 '13 at 22:17
  • No. It is implementation defined (in C++03 at least) weather an uncaught exception causes the stack to unwind. This is why I always catch all exceptions in main (to force the unwind). Then re-throw (to get an OS facilities associated with escaping exceptions). – Martin York Jan 08 '13 at 22:38
  • @Jehjoa: If the choice is calling exit() or throwing an exception throw an exception (this should kill the application). If you can exit with normal control flow even better. You should **only** be calling exit() when something has gone so terribly terribly wrong (like you have completely screwed the stack or heap by writing random garbage to it that the only safe thing is to exit without unrolling the stack). – Martin York Jan 08 '13 at 22:44
  • @Jehjoa: I don't suppose you don't think that your death is an exceptionally significant event of your life? Putting exception cargo cultists' arguments aside, IMO RAII+exception is definitely the way to go in C++. – Lie Ryan Jan 08 '13 at 22:47
  • +1, but also do what Loki says -- catch the exception in `main`. If your implementation happens to unwind the stack on an uncaught exception, and if you really don't care about portability then you *could* omit catching it. But there's no point since it's not exactly difficult. – Steve Jessop Jan 08 '13 at 22:55
  • @gustafr: Oh yes I would care about zero-cost, especially if it thrown once. Zero-cost exception handling applies to what happens when it is not thrown rather than what happens when it is. Not only it exists in real-life, it is a part of Itanium ABI spec. Look it up. –  Jan 08 '13 at 23:12
  • @gustaf r But then you run into trouble if you want to do something that can throw exceptions itself, because destructors should never throw exceptions. You can of course catch them in the destructors, but then you can't use your normal exception handling code for them, which wouldn't be the case if you hadn't thrown an exception to exit the program. – Zyx 2000 Jan 09 '13 at 20:38
  • @Zyx2000, no you can't catch exceptions in a destructor if it wasn't thrown there (or from subsequent calls), but that's not my point. The point is, you can catch the exception in main to unwind the stack, which will destruct all objects you've created so far. Then, you write the deinit code in each of those objects' destructor. To be precise in your answer, what I mean is, you can write that file inside a destructor (which in many ways is rather ugly), or just do it in main, where you catch the exception. It's not dangerous, although I'd leave the flow more naturally, without exceptions. – gustaf r Jan 09 '13 at 22:26
  • @gustafr What I said was that you can catch exceptions in a destructor to prevent them getting thrown out of the destructor. And by "dangerous" I meant that it can be very hard to maintain. – Zyx 2000 Jan 10 '13 at 15:09
1

I'm not even gonna read that SO post, because I know what it says. Don't use exit(), so don't.

I know one reason to use exit() - if you're completely doomed anyway and there's no way you can exit nicely. In such case you will not exit with code zero. So, exit() with non-zero when you're about to crash anyway.

In every other case, create variables which let you leave main loops and exit main nice and sane, to clean-up all your memory. If you don't write code like this, you will e.g. never be able to detect all your memory leaks.

gustaf r
  • 1,224
  • 9
  • 14
1

Will objects elsewhere on the stack still be destructed?

Nope, exit() does the following (in order):

  • Objects associated with the current thread with thread storage duration are destroyed (C++11 only).
  • Objects with static storage duration are destroyed (C++) and functions registered with atexit are called (if an unhandled exception is thrown terminate is called).
  • All C streams (open with functions in ) are closed (and flushed, if buffered), and all files created with tmpfile are removed.
  • Control is returned to the host environment

from: http://www.cplusplus.com/reference/cstdlib/exit/

exit() does not unwind the stack, the memory for the whole stack is simply freed, the destructor for individual objects in the stack are not run. Using exit() is safe only when all objects that does not have simple destructors (those that does not deal with external resources) are allocated in the static storage (i.e. global variables or locally scoped static variable). Most programs have files handlers, socket connections, database handlers, etc that can benefit from a more graceful shut down. Note that dynamically allocated object (that does not deal with external resources) does not necessarily need to be deallocated because the program is about to terminate anyway.

exit() is a feature inherited from C, which does not have destructor and so clean up of external resources can always be arranged using atexit(); in general it's very hard to use exit() in C++ safely, instead in C++ you should write your program in RAII, and throw an exception to terminate and do clean ups.

Lie Ryan
  • 62,238
  • 13
  • 100
  • 144