C ++ New crash principle and Solution
Most C ++ developers use STL extensively in their code. If you use STL and Visusal Studio 6.0 directly, your program will be very likely to crash when the memory is low. The reason is that the results of the new operation are not verified. Worse, if the new operation does fail, there is no standard for the feedback-Some compilers will return NULL pointers, and some will throw an exception. In short, if you use STL in an MFC project, note that MFC has its own rules. This article mainly discusses these issues and explains how the default behavior of the latest Visual C ++ compiler has changed, it also provides an overview of some modifications you must make when using Visual C ++ 6.0, so that you can safely use STL even if the new operation fails.
Background
How many programmers will check whether the new operation fails? Is this check frequently required? I have seen some large and complex C ++ projects written in Visual C ++ 6.0, but I have not seen a check on whether the returned result of new is NULL. Please note that the new return NULL check. In Visual C ++ 6.0, the default action when the new operation fails is to return a NULL pointer instead of throwing an exception. In Visual C ++ 2003, NULL is returned when the new of the c Runtime Library fails, but the Standard C ++ Library) when the new in fails, an exception is thrown. What kind of behavior is when New fails depends on whether the linker is in front of the Standard C ++ library or the C Runtime Library. If the standard C ++ library is in front, an exception is thrown. If the C Runtime Library is in front, only NULL is returned. To rewrite this behavior and force the new that will throw an exception, we need to display the link thrownew. obj. In Visual C ++ 2005, 2008, and 2010, unless the link nothrownew. obj is displayed, an exception is thrown in both the C Runtime Library and the Standard C ++ library. In addition, the behavior described here does not involve hosting code or. NET Framework. If the original Visual C ++ 6.0 code is not expected to throw an exception in the new operation, after porting all the code to the high-version compiler, an exception will be thrown in the new one, the program may be terminated unexpectedly during running. Pay attention to this point.
The C ++ Standard specifies that the new operator must throw an exception upon failure. Specifically, this exception must be std: bad_alloc. This is only a standard. For more information about Visual C ++, see the following table:
Pure C ++ MFC
Visual C ++ 6.0 |
Returns NULL. |
CMemoryException |
> 6.0 |
Std: bad_alloc |
CMemoryException |
It can be seen that the exception thrown in the MFC environment is not required by the C ++ standard. If you use catch (std: bad_alloc) in STL to handle memory allocation failures, this can only be done in environments without MFC. STL in Visual C ++ 6.0 uses catch (...) To handle new failures. This method can work normally in MFC.
Returns the NULL new operator.
In two cases, you do not need to check whether the pointer returned by new is NULL: new will never fail or new will throw an exception.
Even if you think that new will never fail, it is a bad programming habit to not check the return value. Desktop applications are unlikely to suffer from memory depletion. However, applications that require 24 hours of operation on some servers may run out of memory, especially on a shared application server. If you cannot guarantee that your application will never leak a byte, the chance of memory errors will increase.
If you do not check whether the returned pointer is NULL because new throws an exception, this is also excitable. After all, the C ++ standard requires that new should throw an exception upon failure, but this is not the default method of Visual C ++ 6.0, it will only return a NULL pointer. Although later versions support the C ++ standard, the practices in section 6.0 (especially when used with STL) may cause problems. In STL, it is assumed that an exception will be thrown when new fails, regardless of the compiler used. In fact, if new does not show this behavior and gets a NULL pointer due to memory allocation failure, the next behavior of STL will be unpredictable, and the program may crash.
Standard Template Library
Developers are increasingly dependent on STL in the C ++ development process. STL provides many classes and functions based on the C ++ template. STL has several advantages: first, this library provides a consistent interface for a variety of common tasks; second, this part of code has been widely tested, therefore, we can think that it has no bugs. Finally, the algorithms in it are also the best.
To enable STL, the compiler must support the C ++ standard. The Visual C ++ compiler is pre-installed with an STL, which can be used by other manufacturers.
Visual C ++ 6.0 and new operators
If new fails, NULL is returned. This behavior is considered a Bug because it is inconsistent with the standard. All STL implementations, including those included in Visual C ++, are expected to throw an exception when the new operator fails. Although it can change the behavior of new so that it throws an exception when encountering an error, this will bring more irregularities. The following code describes the problem:
1.
#include < string >
2.
void
Foo()
3.
{
4.
std::string str(
A very big string
);
5.
}
6.
In Visual C ++ 6.0, the above Code will eventually call the following function in STL (excerpt, for the convenience of illustration, the redundant code has been removed ):
01.
void
_Copy(size_type _N)
02.
{
03.
...
04.
_E *_S;
05.
_TRY_BEGIN
06.
_S = allocator.allocate(_Ns + 2, (
void
*)0);
07.
_CATCH_ALL
08.
_Ns = _N;
09.
_S = allocator.allocate(_Ns + 2, (
void
*)0);
10.
_CATCH_END
11.
...
12.
_Ptr = _S + 1;
13.
// ACCESS VIOLATION
14.
_Refcnt(_Ptr) = 0;
15.
...
16.
}
17.
In the try statement block, the return value of allocator. allocate is assigned to the local Variable _ S, while allocator. allocate uses new. The default behavior of Visual C ++ 6.0 is: NULL is returned when the new operator fails, which makes the value of _ s null. The next line will assign the value of _ S + 1 to _ Ptr. If _ S is NULL, _ Ptr is 0x00000001. The next sentence _ Refcnt (_ Ptr) = 0 actually Returns _ Ptr-1 (I .e. _ Ptr [-1]), that is, it is actually in the calculation of the NULL originally returned. _ Refcnt returns a NULL pointer, and then assigns 0 to it (* NULL = 0), which immediately generates an access conflict error. Although this seems to be a Bug, the STL code is actually no problem, just to get a correct behavior, it needs new to throw an exception.
Let's look at the execution process that throws an exception when new fails. Execute allocator. allocate first. If new fails, the std: bad_alloc exception will be thrown, and then _ CATCH_ALL will be executed and try again. If the second allocation fails, another std: bad_alloc exception will be thrown, which will be propagated all the way to our code, leading to std :: although the stting object is defined, it is still null.
Modify the new operator
01.
#include < new >
02.
#include < new.h >
03.
#pragma init_seg(lib)
04.
namespace
05.
{
06.
int
new_handler(
size_t
)
07.
{
08.
throw
std::bad_alloc();
09.
return
0;
10.
}
11.
12.
class
NewHandler
13.
{
14.
public
:
15.
NewHandler()
16.
{
17.
m_old_new_handler = _set_new_handler(new_handler);
18.
}
19.
~NewHandler()
20.
{
21.
_set_new_handler(m_old_new_handler);
22.
}
23.
private
:
24.
_PNH
m_old_new_handler;
25.
} g_NewHandler;
26.
}
// namespace
27.
If the above Code is included in our project, the error handling will be automatically modified when new fails, and std: bad_alloc will be thrown in this example.
New (std: nothrow) throws an error
In Visual Studio 6.0, if the above Code is included and the new (std: nothrow) is used for memory allocation, an error is reported when release is run, and the Abnormal program termination is displayed. This is a matter of detail, due to compiler optimization. You can go to Project Settings | C/C ++ | General | Optimizations to disable optimization to avoid this problem, or you can write a new (std: nothrow) by yourself) (See the source code NewNoThrow. cpp ).
Summary
The new operations provided by Visual C ++ 6.0 by default are not compatible with STL. Even if some solutions are mentioned above, it may be difficult to use a third-party library or some other functions in STL. In VC 6.0, the imbalance between new, new (std: nothrow) and STL cannot be completely solved. However, if the above method is not used, it will certainly be very troublesome.
In the MFC project, whether the new field in STL can withstand the exception test depends entirely on how the error is written during error handling in STL. Most will use catch (...) Instead of catch (std: bad_alloc), but this is not necessary.
Finally, as mentioned at the beginning, Visual C ++ 2005 to 2010 have fixed these problems.
Instance: