Basically, use dynamic memory allocation only if want an object to live beyond the lifetime of the scope when create it in. For example, function return the pointer.
/* The C way to request dynamic memory from the heap */ int *i = (int*) malloc(sizeof *i); double *d = (double*) malloc(sizeof(double) * NUM); free(d); free(i); // The C++ way int *i = new int(0); double *d = new double[NUM]; delete []d; delete i;Advantages:
new int[0]
and
delete 0
. No need test guide against NULL before
delete. In fact, under most implementations, if add the
explicit test, it will be carried out twice.
Don’t mix new
/delete
with
malloc
/free
. The result is
undefined.
delete
. One reason is that the operand
of delete
need not be an lvalue.
T* p = new T; T* q = p; delete p; delete q; // error! delete p+1; delete f(x);If zeroing out pointer is important, consider using a destroy function. Passing the pointer as a reference (to allow the pointer to be zero'd out) has the added benefit of preventing
destroy()
from being called for an rvalue.
template<class T> inline void destroy(T*& p) { delete p; p = NULL; } // error: trying to pass an rvalue by non-const reference destroy(f()); destroy(p+1);
In ANSI C++, when new
cannot satisfy a
request for memory, it calls the new-handler function. Not
once, but repeatedly until it can find enough memory. If the
new-handler is NULL (as by default), it will throw
std::bad_alloc
exception. It is a lot more
attractive than wrapping every use of new
inside try
block, so it is common to use this
way.
<setnewh.cpp>
has suggested
not to use standard set_new_handler
defined in
&lr;new>
but use the
_set_new_handler
defined in new.h
.
It use its own way of memory allocation error handling. So
conceal the platform difference by
#ifdef _MSC_VER // New handler for Microsoft Visual C++ compiler #include <new.h> int __cdecl newHandler(size_t size) { // Do whatever return 0; } #else // Ansi C++ new handler #include <new> using namespace std; void newHandler(void) { // Do whatever } #endif void SetNewHandler(void) { #ifdef _MSC_VER _set_new_handler(newHandler); _set_new_mode(1); // Re-route malloc failures to new handler! #else set_new_handler(newHandler); #endif }User-defined new-handler should:
set_new_handler
, if
current new-handler can't make any more memory available.
Next time new
will call the most recently
new-handler.
set_new_handler
. new
will throw
std::bad_alloc
exception. try
/
catch
can be used to handle out of memory
error.
std::bad_alloc
or its derived class (Throwing other type of exception will
violate new
exception specification).
abort
or
exit
. This is how the original new-handler
doing.
#include <new> #include <iostream> #include <cassert> // for debug version, not production code using namespace std; void NoMoreMemory(void) { cerr << "out of memory"; abort(); } void main(void) { set_new_handler(NoMoreMemory); int *pBigDataArray = new int[100000000000000]; // ok. well, so BIG! assert(pBigDataArray); cout << "new int[100000000000000] successfully" << endl; pBigDataArray[0] = 1; pBigDataArray[99999999999999] = 9; cout << pBigDataArray[0] << endl; // 1 cout << pBigDataArray[99999999999999] << endl; // 9 delete [] pBigDataArray; pBigDataArray = new int[100000000000000000000]; assert(pBigDataArray); cout << "new int[100000000000000000000] successfully" << endl; cout << "terminate normally"; delete [] pBigDataArray; return 0; }
When test in Linux GCC, there is compile error when try to allocate
more than 536870911 int
and it also cause runtime
error (trigger _newHandler
). Since int
is 4 bytes, total memory allowed to allocate is 2147483644 bytes,
or >2GB (2147483648 bytes). Also, #include <new>
is optional.
#include <new> // new_handler function typedef already defined using namespace std; template<class T> class NewHandlerSupport { public: static new_handler SetNewHandler(new_handler p); static void* operator new(size_t size, new_handler p=NULL); private: static new_handler currentHandler; }; // in implementation file template<class T> new_handler NewHandlerSupport::currentHandler; // set to 0/NULL by default template<class T> new_handler NewHandlerSupport::SetNewHandler(new_handler p) { new_handler oldHandler = currentHandler; currentHandler = p; return oldHandler; } template<class T> void* NewHandlerSupport::operator new(size_t size) { // prevent derived class call base class new, also ensure to return // legitimate pointer even 0 byte requested as sizeof a // freestanding class never 0 if (size != sizeof NewHandlerSupport) return ::operator new(size); new_handler globalHandler = set_new_handler(currentHandler); void *memory; try { // call the global new, which may invoke currentHandler memory = ::operator new(size); } catch (bad_alloc&) { // if ultimately fail to resolve set_new_handler(globalHandler); // restore handler throw; } set_new_handler(globalHandler); // restore handler return memory; } //client class ClassName : public NewHandlerSupport{ // ... }; // usage in application void NoMoreMemory1(void); void NoMoreMemory2(void); ClassName::SetNewHandler(NoMoreMemory1); ClassName *pc1 = new ClassName; // call NoMoreMemory1 if fail string *ps = new string; // call global new-handler if there is one ClassName *pc1 = new (NoMoreMemory2) ClassName; // call NoMoreMemory2 if fail ClassName::set_new_handler(NULL); ClassName *pc2 = new ClassName; // throw bad_alloc
operator new is that it's inherited by subclasses
Detected memory leaks! Dumping objects -> {685405} normal block at 0x09908790, 88 bytes long. Data: << , > 3C 80 90 09 AC 8D 90 09 2C 8A 90 09 80 01 00 00 strcore.cpp(118) : {544} normal block at 0x00B05690, 28 bytes long. Data: < WL_S> 9C 01 00 00 0F 00 00 00 0F 00 00 00 57 4C 5F 53 {540} normal block at 0x00B056E0, 44 bytes long. Data: < 0X 0V LW > A0 12 B0 00 30 58 B0 00 30 56 B0 00 4C 57 B0 00 Object dump complete.
_crtBreakAlloc
or _afxBreakAlloc
) in the Watch, by default its
value is -1. Set it to the allocation request number that is
interested in (use the smallest number first so can trace down).
Press F5 to continue. Repeat this for each allocation request
number.
Visual C++ Debug version provide other features to detect and check for memory overwrite. For more information look up in MSDN.
If the code hasnew
operations,
delete
operations and pointer arithmetic all
over the place, it will going to mess up somewhere and get
memory leaks, stray pointers, etc. It follows that successful
techniques rely on hiding allocation and deallocation inside
more manageable types.
auto_ptr
when need to return an
object allocated on the free store from a function. This is
an opportunity to forget to delete that object. After all,
it is hard to tell just looking at pointer whether it needs
to be deallocated and if so who is responsible for that.
This is especially important when using exception, where
deallocation done either in the local catch
block (then rethrow) or in user catch
block.
It is troublesome and easy to forget.
void fct(const char* s) { FILE* f = fopen(s, "r"); try { // use f ... } catch (...) { fclose(f); // remember to close the file! throw; } fclose(f); } // resource acquisiton is initialization (RAII) technique class File_handle { public: File_handle(const char* n, const char* a) { p = fopen(n, a); if (p == NULL) throw Open_error(errno); } File_handle(FILE* pp) { p = pp; if (p == NULL) throw Open_error(errno); } ~File_handle() { fclose(p); } operator FILE*() { return p; } // ... protected: FILE* p; }; void fct(const char* s) { File_handle f(s, "r"); // use f ... } // File_handle's destructor closes the file // usage in application try { fct("invalidfile.txt"); } // catch statements ...
More Effective C++ p.59 注2 说: 『1995 七月 ISO/ANSI C++ 标准委员会加入一个新函式:uncaught_exception。 如果一个 exception 正作用中而尚未被捕捉的话,它会传回 true。』 C++ Standard (ISO/IEC 14882) 18.6.4 : bool uncaught_exception(); returns : true after completing evaluation of a throw-exception until either completing initialization of the exception-declaration in the matching handler or entering unexpected() due to the throw; or after entering terminate() for any reason other than an explicit call to terminate(). Note: This includes stack unwinding. 现在我写一个程式测试之。BCB4 和 GCC 有良好表现。VC6 表现不佳。 显然,你说「uncaught_exception 总是传回 false」并不正确。 #0001 #include <iostream> #0002 #include <exception> // only gcc need this file #0003 using namespace std; #0004 #0005 class myexception { }; #0006 #0007 class mytest #0008 { #0009 public: #0010 ~mytest() { #0011 cout << uncaught_exception() << endl; #0012 // 如果此时有 exception 作用中且未被捕捉,会输出 1. #0013 } #0014 }; #0015 #0016 int main() #0017 { #0018 try { #0019 cout << uncaught_exception() << endl; #0020 // 0. 是的,因为未有任何 exception 处於作用状态。 #0021 #0022 throw myexception(); #0023 } #0024 catch (myexception) #0025 { #0026 cout << uncaught_exception() << endl; #0027 // 0. 是的,因为未有任何 exception 未被捕捉。 #0028 } #0029 #0030 mytest t1; #0031 throw myexception(); #0032 // VC6 执行结果:none abnormal program termination (不佳) #0033 // CB4 执行结果:1 Abnormal program termination (佳) #0034 // GCC 执行结果:1 (佳) #0035 }Index