That's right. It's just a "talking", and it's a bit messy. However, even if the context is not smooth, writing it down is not a bad thing. It is helpful to open a volume and to start a pen.
A friend with rich C language development experience asked me a question. A friend asked: "New in C ++ will throw an exception (STD: bad_alloc) When memory allocation fails without returning 0 (some old compilers may still return 0, but such a compiler is "too old"), which is very different from the practice of C programmers. In addition, many c ++ programs do not check this exception when using new to create objects. What is this philosophy ?" He also mentioned: "generally, C programmers always judge the malloc failure, even in the Linux kernel ."
For his doubts, I first thought of the Application Layer Program implemented in C ++. In terms of memory management, it cannot be compared with the kernel program. The OS kernel directly manages the physical memory. The address space of all applications is mapped by it and then translated by its mechanism. If the kernel is not safe in terms of memory management, how can the application layer survive? Memory Allocation in the kernel also needs to consider many other issues, such as the different features of different regions (for example, the buffer used by some DMA should be physically consecutive and located at a specific location ). Equally important: for a mature OS kernel, physical memory and other resources cannot be disclosed even when an application encounters serious problems, and other programs cannot be affected.
Application applications are different. They generally have independent flat virtual memory space, which is usually much larger than physical memory. Therefore, if an application runs out of virtual memory, either it is not enough to estimate the size of the data, or it is a serious bug that must be specifically addressed.
Depletion of virtual memory is the same as many other serious bugs (such as stack corruption caused by buffer overflow). In most cases, even if it is detected, it is often not feasible ", why don't we implement this plan early? Why don't we wait for it to take over? In turn, it is not a bad thing to think about a fast and painful failure. At least, it is much better to continue running for half an hour with a problem, and then create an inexplicable and hard-to-reproduce bug in a completely unrelated place.
This is the answer I gave to my friend at the time. My friend reluctantly agreed, at least not to worry about why C ++ programmers should check STD: bad_alloc when they are not new. However, following this question, I think I can think of many related topics.
(1) The first thing that comes to mind is the Java language practice. All variables in Java are referenced (except for basic types), and the referenced objects are created on heap using new. When a new object is added to Java, java. Lang. outofmemoryerror may also be triggered theoretically. Of course, this is an error, not an "exception" derived from Java. Lang. exception, so the language does not force us to catch it. However, whether or not the language is required is not important. Why is it important that the language is not required. Obviously, if the problem is serious enough, even if the language is not required, the Java programmer will package try/catch around every new place. But Java programmers do not. Why? I think the key reason is the same as above: An application consumes virtual memory, either it is insufficient to estimate the data size (Should I use the-XM series parameters of the Java command to set a larger heap ?), Or a bug that must be specifically addressed.
At the same time, compared with C ++, Java programs use this decision for better reasons: because of the GC mechanism, java programs have less memory leakage due to carelessness (memory pseudo-leakage due to poor design may occur ).
(2) "new" in C ++ is not just as easy as allocating memory. For user-defined types, "New T;" is equivalent to operator new plus the call to the T constructor. Because the constructor of the class can cause an exception, a new statement may still generate an exception even if the memory allocation is smooth. It seems that catch is not limited to STD: bad_alloc.
(3) The "philosophical" factor is not taken into consideration for the moment. If someone still thinks that they should strictly check the memory allocation like the C program, can they? Of course you can. After all, can it throw an exception and capture it if it throws it. As a result, people will naturally think: C ++ or Java programmers are using the ostrich strategy to deal with memory allocation failures. Is it one of the reasons for the usage of exceptions that are troublesome? Obviously, each time the memory is allocated, a try/catch layer is required, which is much more messy than the IF/else style in C for the returned value.
In fact, it is not the correct method to use exceptions. If an exception is just a simple syntax alternative to if/else, it is unnecessary. One of the benefits of exceptions (really just "one") is that it is enough to handle an exception in one place. For example:
void f1() {try {// ...f2();} catch (const some_exception& e) {// ...}}void f2() {// ...f3();}void f3() {// ...f4();}void f4() {// ...throw some_exception();}
F4. F4. F1. F2. F2 and F3. in the middle, the exception is "passed through". (It may need to be declared in Java) -- the reason is probably that they do not have enough context to handle this exception. Therefore, we don't need to start from where the problem occurred to where the problem was handled "down" like using the return value. We need to judge each layer in the middle, to write down the following layers:
x = f();if(x < 0)return x;
. Don't you think this can make most functions clean? In terms of exception or error handling, this also makes the responsibility at different logic levels clearer.
It is worth mentioning that during the exception rollback process, the constructed objects on the stack will be analyzed normally. Of course, this requires programmers to consider the "exceptional security" factor when designing the class.
I can write a book about the idea of exception handling and the use of exceptions. If you are more interested, take a look at the three volumes of predictional written by Herb Suter: predictional C ++, more than tinal C ++, and predictional C ++ style.
(4) In fact, C ++ does not only throw an exception new, but also does not throw an exception new, which is generally referred to as "nothrow new ". You can use it like this:
#include <new>// ...T* p = new (std::nothrow) T(/* ... */);
Among them, nothrow is a constant of the type STD: nothrow_t defined in the header file <New>. We can use it directly. In this case, if the memory allocation fails, the P value will be blank (0) and no exception will be thrown, which is similar to the malloc of C.
Nothrow new is actually an overload of operator new and operator new [] implemented in the standard library. We can also reload operator new/operator new [] according to our own needs. It can be global or overloaded for a class. However, it is rarely used in practice.
Note: When nothrow new is used to create an object, you can only ensure that STD: bad_alloc is not thrown because of the failure of operator new or operator new [], but the constructor of the object cannot throw other exceptions, even STD: bad_alloc is thrown.
(5) Speaking of C ++ memory allocation, it is necessary to mention set_new_handler. It allows you to set a function that can be called back when operator new and operator new [] fail to allocate memory. If you think there is any way to improve the memory usage when STD: bad_alloc occurs, this callback function is also a life-saving tool. For detailed usage and instructions, refer to the following: http://new.cplusplus.com/reference/std/new/set_new_handler/
(6) Although STD: bad_alloc is often left unattended, not all exceptions are the case, and some exceptions can be handled to recover the loss. Therefore, at the end of the main function, or before the multi-threaded program, especially the so-called worker thread function exits, use "catch (...) it is good to capture all exceptions. Even if you don't want to recover anything, at least do not stop the entire program because of a thread and try to ensure data integrity.
But don't expect catch (...) to capture all "problems" or "Bugs". It's not that good. It can only capture exceptions in C ++. Other problems, such as stack corruption mentioned earlier, and access by wild pointers, are easily detected. (I 've been bored by the wild pointer recently, but I didn't write it .)
A thread crash usually leads to the entire process crash. For this reason, some people prefer to use multi-process, especially in Unix-like environments. Although I personally do not disagree with this, I do not particularly agree, because debt is always to be paid back. This also includes "technical debt": if there is a bug, we still need to find the root cause and solve it, even if it is "really bypassing.
However, there are other advantages to using multi-process because sharing data between processes is much more troublesome than sharing data between threads of the same process.ForceDevelopers are designed to reduce sharing, which can reduce concurrency issues and improve concurrency efficiency. After entering the multi-core era, it is more important to make concurrent entities as independent as possible to give full play to the parallel performance of hardware.
(7) Another good friend and colleague of mine (Sina Weibo @ don't talk) Thought: Program crash is not so terrible. It may be the most intolerable bug for most customers, but it only stems from social psychology and is not necessarily the most serious bug.