探讨operator new和new operator

来源:互联网 发布:java 并发和线程 编辑:程序博客网 时间:2024/05/29 16:36

探讨operator new和new operator

1. 基础知识

  1. new operator:指我们在C++里通常用到的关键字,比如A* a = new A
  2. operator new:它是一个操作符,并且可被重载(类似加减乘除的操作符重载)

对于如下一个表达式,就是new operator的一个简单的例子。

A* a = new A;

其具体过程分为如下三步。

1.分配内存,2.调用构造函数构造对象,3. 返回分配指针。

对于分配内存这一操作就是由operator new来完成的,然后会根据参数来选择合适的构造函数,如果不加参数则是调用默认构造函数(如上例子)

2. operator new的三种形式

#include <new>//C++98版本//throwing (1)  void* operator new (std::size_t size) throw (std::bad_alloc);//nothrow (2)   void* operator new (std::size_t size, const std::nothrow_t& nothrow_value) throw();//placement (3) void* operator new (std::size_t size, void* ptr) throw()//C++11版本//throwing (1)  void* operator new (std::size_t size);//nothrow (2)   void* operator new (std::size_t size, const std::nothrow_t& nothrow_value) noexcept;//placement (3) void* operator new (std::size_t size, void* ptr) noexcept;

首先要了解到C++给我们提供了global operator new 操作,就是如上的三种形式,默认情况下,我们一般都是调用的这三种operator new操作之一。C++11和C++98的区别只是在于关键字noexcept的使用。

(1)(2)的区别仅是是否抛出异常,当分配失败时,前者会抛出bad_alloc异常,后者返回null,不会抛出异常。它们都分配一个固定大小的连续内存。

(3)是placement new,它也是对operator new的一个重载,定义于#include <new>中,它多接收一个ptr参数,但它只是简单地返回ptr。然后后续就在ptr所指地址上构建一个对象(通过调用其构造函数),这在内存池技术上有广泛应用。

以C++11版本为例,三种global operator new的应用如下:

A *p1 = new A;          //使用第一种operator newA *p2 = new(std::nothrow) A;    //使用第二种operator newvoid *p3 = malloc(sizeof(A));   //申请内存A *p3 = new(p3) A;      //使用第三种operator new

new(p) A(),就是在p所指定的地址上调用A的构造函数。这是的p即可是堆中的地址,也可以是栈中的地址。

3. operator new重载

可以根据自己的需要来在相应的类中重载operator new,此时在new关键字作用在这种类型时候,将首先调用类内重载的operator new操作,而不是全局函数 operator new。

4. delete

delete的使用基本和new一致。
delete的具体
包括operator delete的重载方式这些都相似,只不过它的参数是void*,返回值为void。

5. set_new_handler

当operator new申请一个内存失败的时候,它会进行如下的处理步骤:
1. 如果存在客户指定的处理函数,则调用处理函数(new_handler),如果不存在则抛出一个异常。
2. 继续申请内存分配请求。
3. 判断申请内存是否成功,如果成功则返回内存指针,如果失败转向处理步骤1

内存处理例程通过set_new_handler来进行设置

namespace std{     typedef void (*new_handler)();     //无异常抛出     new_handler set_new_handler(new_handler p) throw();}

其中new_handler是个typedef,定义一个函数指针,该函数没有参数,也没有返回值;set_new_handler用于设置处理函数,设置p为当前处理函数,并返回之前的new_handler。

默认情况下,系统并不会设置new_handeler,为0,而是交给客户端来自行设计。当没有new_handler时候,就会直接抛出异常或者结束。如果存在new_handler函数,就会先调用new_handler,函数结束后再直接申请内存,如果仍然无法申请,继续调用new_handler,直到成功。

一个设计良好的new_handler必须做以下事情:

  1. 删除其它无用的内存,使系统具有可以更多的内存可以使用,为下一步的内存申请作准备。实现此策略的办法是:程序一开始执行就分配一大块内存,当new_handler被调用时,将它们释放还给程序使用。
  2. 设置另外一个new_handler。如果当前的new_handler不能够做到更多的内存申请操作,或者它知道另外一个new_handler可以做到,则可以调用set_new_handler函数设置另外一个new_handler,这样在operator new下一次调用的时候,可以使用这个新的new_handler。
  3. 卸载new_handler,使operator new在下一次调用的时候,因为new_handler为空抛出内存申请异常。
  4. new_handler抛出自定义的异常
  5. 不再返回,调用abort或者exit退出程序

我们可以直接在一个函数中调用set_new_handler,来设定相应的内存不足处理函数,但是这并不是针对某个类的,而是所有的operator new调用失败。

但是如果需要,可以重载operator new,自己实现这个行为。

需要重载相应类的operator new来进行设定:

  1. 首先调用标准的set_new_handler,自定义专属类的处理函数
  2. 调用::operator new,执行实际的内存分配。如果内存分配失败,刚才被安装的new_handler将被调用。

3.无论new成功还是失败,都必须在类自定义的operator new结束前恢复全局new_handler

原创粉丝点击