1

When we declare a std::vector, or std::string, ..., like that for example

std::string hello("Hello");

isn't it wrong? shouldn't we do

std::string hello;

try {
    hello = "Hello";
} catch (std::exception &e) {
    std::cout << e.what() << std::endl;
    return (-1);
}

Because if I understood how it works, when an allocation fails, it will result in an uncaught exception otherwise, so why do I often see code like this ? :

std::string s("hello");

s += " world";
s += "!";
s.reserve(100);
...

without any check?

Fayeure
  • 1,181
  • 9
  • 21
  • 3
    *it will result in an uncaught exception* -- `int main() { try { your program } catch (std::exception& e) { ... }}` -- Where will the exception be uncaught? Your question is based on a false assumption. – PaulMcKenzie Oct 24 '21 at 13:42
  • 1
    Please note that, due to [short string optimization](https://stackoverflow.com/questions/21694302/what-are-the-mechanics-of-short-string-optimization-in-libc), your example unlikely involves any allocation. – Bob__ Oct 24 '21 at 13:53
  • 2
    Running out of heap or stack, even if detected and handled in some graceful fashion, have pretty limited options for recovery. And with modern architectures and operating systems that allow overallocation the out-of-memory may be detected far down the road from where the (promised) allocation happens. – Eljay Oct 24 '21 at 13:53
  • Because of Weinberg’s Law: "If builders built buildings the way programmers wrote programs, then the first woodpecker that came along would destroy civilization." – Öö Tiib Oct 24 '21 at 14:05
  • In addition to all the answers which are right, I wanted to add that I discovered a C++ application on iOS never receive `std::bad_alloc` exception. Instead, the allocation routine directly calls `abort()` in case of memory exhaustion, giving no chance to recover. – prapin Oct 24 '21 at 14:36
  • "Over-catching" of exception makes for very unreadable code (as does "over-throwing", incidentally). You should catch exceptions where you can handle them in a meaningful way, which is usually not immediately. – molbdnilo Oct 24 '21 at 14:37
  • @PaulMcKenzie I mean it results in an uncaught exception if you dont use a try catch – Fayeure Oct 24 '21 at 15:17
  • @Fayeure -- So each and every line that may allocate memory, you're going to surround with `try / catch`? So let's say you catch this error, what are you going to do? The only thing is (hopefully) your program is in any state to write something to the screen, log file, etc., and then exit. – PaulMcKenzie Oct 24 '21 at 15:35
  • @PaulMcKenzie I just want to be able to call all destructors from the main scope and do a clean exit if I catch the exception, that's all I want to do, and when an exception is uncaught it basically just calls `abort` and the destructors are't called – Fayeure Oct 24 '21 at 20:04
  • @Fayeure If you really want destructors to be called, then you can use a single try-catch block within `main`. You don't have to catch every potential throw separately. That said, leaving the exception uncaught and thus not unwinding the stack can be quite useful since it allows the creation of a meaningful core dump that can a convenient way to debug why the exception was thrown. As such, I recommend considering why you think you want the destructors to be called, and then to reconsider whether that's worth losing the convenient core dump. – eerorika Oct 24 '21 at 20:17
  • @eerorika Yeah but I saw on another post that it's not a good practice to do that apparently, because try / catch blocks should be used only at specific places where you do allocations/ do stuff that could go wrong – Fayeure Oct 24 '21 at 20:25
  • 1
    @Fayeure That's just bad advice. Catch blocks should be used only at places where you can recover from an exception. If you cannot recover from an exception, then it's best practice to let it propagate up the call stack to a calller that can recover from it. And if it cannot be recovered at all, then the program is terminated and you get a core dump that you can debug and find out what went wrong. – eerorika Oct 24 '21 at 20:28

4 Answers4

4

Allocation failure is rarely checked because there is rarely a reasonable way to recover from it. What should the program do when there is no memory to do what the program needs to do? Usually, the only reasonable response is to stop what you're doing, which is exactly what happens when you don't catch the exception.

Furthermore, some language implementations don't necessarily even fail to allocate since they may over allocate memory. Such system will delay actual allocation until when the memory is actually accessed. As such, even if you did have a clever strategy to handle lack of memory, you cannot rely on allocation failure to detect such case, unless you know that the target system doesn't over allocate.

eerorika
  • 232,697
  • 12
  • 197
  • 326
  • Herb Sutter had a good article about overallocation, but I can find a link to it right now (drat). The upshot of the article was "not much can be done defensively to prevent or handle the problem". – Eljay Oct 24 '21 at 13:57
  • So that means that it is impossible to write clear code in C++ without becoming unreadable? – Fayeure Oct 24 '21 at 15:26
  • @Fayeure Do you think micromanaging memory allocations with an umpteen try/catch blocks is readable? Do you know what your code would look like with such paranoid coding style? – PaulMcKenzie Oct 24 '21 at 15:36
  • @Fayeure I don't see how you came to that conclusion. – eerorika Oct 24 '21 at 16:26
  • @eerorika I mean in C every piece memory we allocate can be protected, it feels wrong that it can't in C++. – Fayeure Oct 24 '21 at 19:59
  • @Fayeure It's exactly the same in C. `malloc` won't return null on systems that over allocate memory. And hardly any C programmer checks mallocated pointer for nullness. They typically would instead indirect through that null resulting in undefined behaviour, which is frankly worse than the controlled exception in C++. – eerorika Oct 24 '21 at 20:00
  • @eerorika Checking Maybe `malloc` won't return `NULL` on some systems, but it's the standard so a good C programmer should always check for nullness after a `malloc`, and that's just when I want to do here by calling all destructors if an allocation failed – Fayeure Oct 24 '21 at 20:12
  • @Fayeure Returning null on allocation failure being standard is irrelevant if the allocation never fails - which it never does when memory is over allocated. Whether the programmers should or shouldn't check mallocated pointer for nullness, most of them don't check in my experience. And the reasons for them not doing so are probably mostly the same as why C++ programmers don't wrap every potentially allocating operation (which includes nearly every function call) in try-catch. – eerorika Oct 24 '21 at 20:22
  • @eerorika `On error, these functions return NULL.` from man malloc – Fayeure Oct 24 '21 at 20:28
  • @Fayeure As I said, that is irrelevant. Null is returned **only** if there is an error. If there is no error, then it won't return null. And there is never an error due to lack of memory when the memory is over allocated (unless perhaps virtual memory space runs out, but that's quite a-typical in 64 bit world). – eerorika Oct 24 '21 at 20:29
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/238490/discussion-between-fayeure-and-eerorika). – Fayeure Oct 24 '21 at 20:31
1

Most people know how memory-intensive their app is, but what are you going to do if you can't allocate a string? You probably also at that point can't do much of anything, and you're going to have to exit.

When you have actual recovery you can do, people will probably catch all exceptions at a higher level -- several method calls back, and then do what they can. Hopefully by rolling back the stack a bit, you'll release enough resources you can do whatever error handling you'd like before exiting.

Joseph Larson
  • 8,530
  • 1
  • 19
  • 36
0

The prize question is: what do you do if you catch such an error? Unless you've got some special circumstance where you are able to recover from such an error, there is simply no point in catching it. Even in your example you simply return an error code - basically doing what the exception would have done for you otherwise, which is their entire point. You're probably just more comfortable with error returns than exceptions, which is why you prefer that scenario.

If such an exception is thrown, the next step is almost always to fail the routine that encountered it. Doing so by letting the exception bubble up is the best, least verbose way to do that. If you want to prevent crashes, add a try-catch at the entry function of whatever thread you're in, which prints the exception and exits gracefully. But, especially for debugging/testing, that often just hides useful information without providing much benefit. I prefer only catching specific errors that I can recover from in some way.

Wutz
  • 2,246
  • 13
  • 15
  • it doesn't only prints the error message, it also exits the function, which clears the stack and destructors get called – Fayeure Oct 24 '21 at 15:21
  • All of which also happens when the exception is not caught. – Wutz Oct 24 '21 at 18:32
  • I don't think so, I tested creating a class that displays a message in constructor and destructor and throwing an exception after creating an instance of that class and it just called `abort` and the program stopped without calling the destructor – Fayeure Oct 24 '21 at 19:56
  • There could be multiple reasons for why you didnt see the message as the program was terminating. But i can guarantee you that throwing exceptions frees the stack and calls destructors. Otherwise they would be useless. See also https://stackoverflow.com/questions/2331316/what-is-stack-unwinding/2332865 – Wutz Oct 25 '21 at 01:18
  • so if the stack is guaranteed to be freed that's all I want, but then why the messages don't display? Maybe because the stack is unwinded after the program termination? – Fayeure Oct 25 '21 at 13:38
  • Its possible that the unwinding is skipped because the entire program is crashing anyway. There is no need to free resources, as the OS will do it anyway. You would have to read the Standard to find out what behavior it guarantees when an exception is thrown from main. Or it could simply be that your console output is not flushed before the program terminates. A breakpoint in the destructor could help you figure this out. – Wutz Oct 25 '21 at 19:05
0

It's a pain to wrap everything that can throw an exception in try/catch. Unless you anticipate the failure and know how to recover from it, most often you don't. Memory allocation errors like this are hard to recover from. If you don't catch the exception yourself, the stack will unwind all the way to the program's entry point and e.what() will be printed before the program aborts.

qz-
  • 674
  • 1
  • 4
  • 14
  • So then why when I print a message in my destructors the messages are not displayed before the abort? – Fayeure Oct 25 '21 at 13:39