c++ Object-Oriented operator new Learning Notes

Keywords: C++ Programming

new is rarely seen in actual code, even in older c++ compiler environments, auto_ptr manages memory.
But after all, several examples of c++ close to the bottom are that there is no additional burden on class encapsulation, and the c++ object model is still mapped to continuous memory addresses one by one.
For memory allocation, control is still in the hands of programmers. It is necessary to familiarize yourself with it.

Overload operator new benefits

  1. Used to detect errors in operation: for example, the more common section error of array crossing bounds, sometimes the system may not understand dump, then the error is hidden. Use this to achieve memory leaks.

  2. Providing performance: This is the most important point. The system provides a model of memory allocation behavior in a one-size-fits-all manner. Different data have different needs for memory allocators, and the system does not necessarily provide the best.

  3. Collect and use statistical data.

set_new_handler

Like operator new, it is provided for invoking execution after bad_alloc fails in memory allocation.
The prototype provided by the system is as follows. The default global return pointer is nullptr, which can be overloaded or placed inside the class if necessary.

std::new_handler set_new_handler(new_handler); 

Normally this pointer std::new_handler will not be assigned globally, if this will undoubtedly affect other modules, people can not allocate the best bad_alloc memory.
As soon as the global pointer is assigned, you don't know where to execute anything. Therefore, self-use and self-return become the basic principle. The basic process is as follows:

  1. Get the original global pointer new_handler and store it

  2. Set to use the new_handler

  3. When you run out (within scope/class), set the restitution of the original saved items to the system.

Of course, there is also the issue of multithreading, which is not relevant to the current discussion. The management of this resource is finally returned, which can be accomplished by using class constructor/destructor. The basic form is as follows.

class NewHandlerHolder {
public:
    //Constructors and destructors work together to automatically manage resources.
    explicit NewHandlerHolder(std::new_handler nh) 
        : handler(nh) {}
    ~NewHandlerHolder() {
        std::set_new_handler(handler);
    }   
private:
    std::new_handler handler;
};
 
class Widget 
{
public:
    void *operator new(size_t size) throw(std::bad_alloc);
 
private:
    static std::new_handler currentHandler;
};
 
std::new_handler Widget::currentHandler = 
    [](){
        cout<<"Widget: Unable to satisfy request for memory\n";
        set_new_handler(nullptr);
    };  
 
void *Widget::operator new(size_t size) throw(std::bad_alloc) { 
    cout<<"begin Widget::operator new\n";
    NewHandlerHolder h(std::set_new_handler(currentHandler));
    return ::operator new(size);
}

Overloaded object operator new

In the simplified version, only one operator new form is restarted. It is reasonable to avoid inconsistencies in behavior (some call global and some call word construction) or to overload all of them.

class INTType
{     
public: 
    INTType():x(0) {}
    static void *operator new(size_t size) throw(std::bad_alloc);
      
    //...
private:
    int x;
};    

operator new best practices for overloaded objects

If there are several classes in the system that need to overload the global operator new, in fact, it can be abstracted into a class as the base class, and the subclass inherits the interface.
If the requirement is not only to save code, but also to have an exclusive share of the instance data of each class, the class programming template class can be used, so that the subclass instantiation data are independent.

template<typename T>    // T is used for instantiation, and the class itself is not used
class NewHandlerSupport {
public:
    void *operator new(size_t size) throw(std::bad_alloc);
    void *operator new[](size_t size) throw(std::bad_alloc);
 
protected:
    static std::new_handler currentHandler;
};
// Inheritance using classes
class WidgetHander : public NewHandlerSupport<WidgetHander>

summary

There is no need to overload anything in the whole world. It has a larger impact. It's better to overload it in classes.
At present, there should be a good and mature scheme for memory statistics and recording. In addition, due to the introduction of smart pointers, memory leaks rarely occur in the use of memory.
The remaining strong demand for operator new overload comes only from performance improvements, which is convenient for further study of boost::Pool

Reference: <EC49/50/51/52>

Posted by arnihr on Sat, 06 Apr 2019 11:15:31 -0700