C + + memory is managed manually by programmers, unlike Java or. NET, which has a garbage collection mechanism. C + + memory management is primarily the allocation routines and return routines (allocation and deallocation routines), operator new and operator Delete, and a mated role New-handler. When it comes to arrays, the operator new and operator delete mentioned above will change to operator new[] and operator delete[].
Memory management is more complex in a multithreaded environment because heap is a global resource that can be changed, and multiple threads accessing the same resource will have race conditions (race state). Use static type variables to be more careful. If you do not have proper synchronization control (synchronization), you may break the heap data structure when using the lock-free (lock-free) algorithm or by carefully preventing concurrent access (concurrent access).
Finally, note that the heap used by the STL container is managed by the allocator object owned by the container (allocator objects), not directly managed with new and delete.
Article 49: Understanding the behavior of New-handler
When operator new fails to satisfy a memory allocation, it is thrown one time. It used to return a null pointer, and now some legacy compilers do the same.
Before operator new throws an exception, a client-specified error handler is called First: New-handler. (This is not all, operator new is more complex, see * * clause **51). The client uses Set_new_handler to specify the function "to handle out of memory", which is the standard library function declared in new
namespacestd{ typedefvoid(*new_handler)(); throw(); }
New_handler is a function pointer that has no arguments or returns anything. Set_new_handler is to set a new_handler and return a new_handler function, and the New_handler returned is the Set_new_handler function that is being executed before New-handler is called. The throw behind is an exception detail indicating that the function does not throw an exception. You can use this
void outOfMem() { std::cerr<<"Unable to satisfy request for memoryn"; std::abort(); } int main() { std::set_new_handler(outOfMem); int *pBigDataArray=newint[100000000L]; …… }
If operator new cannot allocate enough space for 100 million integers, Outofmem will be called.
When operator new fails to satisfy the memory request, it constantly calls New-handler until enough memory is found. Repeated calls to the code are discussed in * * clause **51. Here first, well-designed New-handler must do the following things;
- allow more memory to be used. this can cause the next memory allocation action within operator new to be successful. One approach is to allocate a large chunk of memory at the beginning of the program and release it when New-handler is first called.
- install another new-handler. when the current new-handler cannot get more memory, it may not be until which New-handler has this ability.
- Remove the New-handler. The null pointer is passed to Set_new_handler, and once no new-handler,operator new is installed, an exception is thrown when the memory allocation is unsuccessful.
- throws an exception Bad_alloc (or derived from Bad_alloc). Such exceptions are not captured by operator new and are therefore not propagated to memory demands.
- does not return. usually abort or exit.
Sometimes, we want to handle memory allocation failures and class-related situations. For example
class X{ public: staticvoidoutOfMemory(); …… }; class Y{ public: staticvoidoutOfMemory(); …… }; X* p1=new X;//分配不成功,调用X::outOfMemory Y* p2=new Y;//分配不成功,调用Y::outOfMemory
C + + does not support class-specific new-handler, but we can implement this behavior ourselves. Let each class provide its own set_new_handler and operator new. ................................................................................................
Now it's time to deal with the failure of the widget class memory allocation. First, there is a call function that operator new cannot allocate enough memory for the widget, that is, the New_handler function
classwidget{ Public:Static STD:: New_handler Set_new_handler (STD:: New_handler p)Throw();Static void*operator New(STD:: size_t size)Throw(STD:: Bad_alloc);Private:Static STD:: New_handler Currenthandler; };STD:: New_handler widget::currenthandler=0;STD:: New_handler Widget::set_new_handler (STD:: New_handler p)Throw() {STD:: New_handler Oldhandler=currenthandler; Currenthandler=p; REUTRN Oldhandler; }
The operator new for the
widget does the following:
1, call the standard Set_new_handler, and tell the widget about the error handling function. This installs the widget's New-handler as global New-handler.
2, call global operator new, if it fails, global operator new will call the widget's New-handler because of the first step. If global operator new eventually fails to allocate enough memory, a Bad_alloc exception is thrown. The widget operator new to restore the original global New-handler, and then propagate the exception.
3, if the global operator new call succeeds, the widget's operator new returns a pointer to the allocated memory. The widget destructor manages the global New-handler, which restores the global New-handler before the widget's operator new is called.
class NewHandlerHolder{ public: explicitNewHandlerHolder(std::new_handler nh) :handlere(nh){} ~NewHandlerHolder() { std::set_new_handler(handler); } private: std::new_handler handler; NewHandlerHolder&(const NewHandlerHolder&);//防止copying operator-(const NewHandlerHolder&); };
This makes the widget's operator new implementation simple
void* Widget::operatornew(stdthrow(std::bad_alloc) { NewHandlerHolder h(std::set_new_handler(currentHandler));//安装Widget的new-handler return ::operatornew(size); }
Widget customers should use their new-handling like this
void outofmem (); Widget::set_new_handler (OUTOFMEM); //set Outofmem function as widget Widget* pw1= new widgets; //memory allocation failed, call Outofmem std::string * ps= new std ::string ; //memory allocation failure call Global new-handling (if any) Widget::set_new_handler (0 ); //set widget exclusive new-handling is null Widget* pw2= new widgets; //memory allocation failure immediately throws an exception
The class code that implements this scheme is basically the same, and it is a good way to reuse a base class base classes. You can use a template base class so that each derived class will get the class data copy of the entity that is different from each other. This base class allows its derived class to inherit it to get Set_new_handler and operator new,template parts to ensure that each derived Class obtains an Currenthandler member variable with an entity that is mutually dissimilar.
Template<TypeNameT>classnewhandlersupport{ Public:Static STD:: New_handler Set_new_handler (STD:: New_handler p)Throw();Static void*operator New(STD:: size_t size)Throw(STD:: Bad_alloc); ......Private:Static STD:: New_handler Currenthandler; };Template<TypeNameT>STD:: New_handler Newhandlersupport<t>::set_new_handler (STD:: New_handler p)Throw() {STD:: New_handler Oldhandler=currenthandler; Currenthandler=p;returnOldhandler; }Template<TypeNameT>void* NEWHANDLERSUPPORT<T>::operator New(STD:: size_t size)Throw(STD:: Bad_alloc) {Newhandlerholder h (STD:: Set_new_handler (Currenthandler);return::operator New(size); }//Initialize each of the Currenthandler to null Template<TypeNameT>STD:: New_handler newhandlersupport<t>::currenthandler=0; With thisclass Template, it's easy to add Set_new_handler to the widget.classWidgets: PublicNewhandlersupport<widget>{...};
The type T is never used in the template base class. Because Currenthandler is a static type, using a template would mean that each class has its own currenthandler. If you are using multiple inheritance, be aware of the content mentioned in * * clause **40.
operator new allocation failure in C + + throws an exception bad_alloc, but the old standard is to return a null pointer. This form of the old standard is nothrow form.
class Widget{}; Widget* pw1=new Widget;//分配失败,抛出bad_alloc if(pw1==null)//判断是否分配成功。但是这个测试失败 Widget* pw2=new(std::nothrow)Widget;//分配失败,返回null if(pw2==null)//可以侦测
The new (Std::nothrow) widget takes two things, the first allocates memory to the Widget object, and if it fails, returns a null pointer. Second, if it succeeds, call the widget's constructor, but what this constructor does, nothrow new does not know, is likely to open up memory again. If the constructor uses operator new to open up memory, it is still possible to throw an exception and propagate. Using nothrow new only guarantees that operator new does not throw an exception, and there is no guarantee that an expression like the new (Std::nothrow) widget does not throw an exception. Therefore, there is no need to use nothrow.
Summarize
- Set_new_handler allows a client to specify a function that is called when the memory allocation is not met.
- Nothrow New is a fairly limited tool because it only applies to memory allocations, and subsequent constructor calls are likely to throw exceptions.
"Effective C + +": Clause 49: Understanding the behavior of New-handler