1. new与operator new

C++中有很多语法让人难以理解,如:new operator(操作符,下同)和operator new之间差异,确切的说,应该是new与operator new 的区别。

1.1 new operator

如下代码:

string *ps=new string("memory management");

这里所使用的new就是所谓new operator,是由C++语言内建的,就像sizeof那样,不能改变意义,总是做相同的事情。

这个动作的含义分为两方面:

第一,它分配足够的内存,用来放置某类型的对象。对于上例而言,它分配足够放置一个string 对象内存。

第二,它调用一个构造函数,为刚才分配的内存中的那个对象设定初始值。

new operator总是做这两件事,无论如何你是不能改变其行为。

1.2 operator new

能够改变的是用来容纳对象的那块内存的分配行为new operator调用某个函数,执行必要的内存分配动作,你可以重写或者重载那个函数,改变其行为。这个函数名称就叫operator new 。

函数 operator new 通常声明如下:

void * operator new (size_t size);

其返回类型void*。即返回一个指针,指向一块原始的、未设置初始值的内存

函数中的size_t参数表示需要分配多少内存,你可以将operator new 重载,加上额外的参数,但第一个参数类型必须总是size_t。

或者你从来没有直接用过operator new ,但是你可以像调用任何其他函数一样地调用它

void* rawMemory=operator new(sizeof(string));

这里的operator new 将返回指针,它指向一块足够容纳string对象的内存。

和malloc一样,operator new 的唯一任务就是分配内存,它不知道什么是构造函数,它只负责分配内存。

取得operator new 返回的内存并将之转为一个对象,是new operator的责任。

1.3 当编译器看到这个句子:

string *ps=new string("memory management");

它必须产生一些代码,或多或少会反映如下行为:

1) void* memory=operator new(sizeof(string));   //取得原始内存,用于放置一个string对象

2) call string::string("memory management") on *memory;//将内存中对象初始化

3) string *ps=static_cast<string*>(memory);   //让ps指向新完成的对象

注意第二步,调用一个构造函数。身为程序员没有权利绕过new operator像这么使用构造函数,但是编译器却是这么干的

这就是为什么如果你想要做出一个heap-based object,一定要用new operator的原因。

也就是说new 出来的东西都放在heap里面,而无法直接调用“对象初始化所必须的构造函数”。

2. placement new

2.1 有时候你真的会想直接调用一个构造函数,针对一个已经存在的对象调用其构造函数,并无意义,因为构造函数用来对象初始化,而对象只能初始化一次。但是你偶尔会有一些分配好的原始内存,你需要在上面构建对象,有一个特殊的地方 operator new 称为placement new,允许这么做。

例如:

class Widget {   public:   Widget(int widgetSize);    ...... };

Widget* constructWidgetInBuffer(void *buffer,int size) {   return new (buffer) Widget(size); }

此函数返回指针,指向一个Widget object,它被构造于传递给此函数的一块内存缓存区上。当程序运行到共享内存或者内存I/O映射。这类函数可能是有用的,因为在那样运用中,对象必须置于特定的地址,或者置于特殊函数分配出来的内存上。

2.2 函数内部

Widget* constructWidgetInBuffer 只有一个表达式new (buffer) Widget(size),

有点奇怪,其实不足为奇,这是new operator的用法之一,指定一个额外的自变量(buffer)作为new operator "隐式调用operator new "。于是,被调用的operator new 除了接受"一定要有size_t自变量"之外,还接受了一个void* 参数,指向一块内存,准备用来接受构造好的对象这样的operator new 就是所谓的placement new

void * operator new(size_t size,void* location)

{

return location;

}

operator new 的目的是要为对象找到一块内存,然后返回一个指针指向它,在placement new 的情况下,调用者已经知道指向内存的指针了,因为调用者知道对象应该放在哪里。因此placement new 唯一需要做的就是将它获得的指针再返回。

至于没有用到(但一定得有)的size_t参数,之所以不赋予名称,为的是避免"编译器某物未被使用"的警告。

另外注意:placement new 是C++标准程序库的一部分,要使用placement new 得用#include<new>,旧式编译器用 #include<new.h>

回头想想placement new ,我们便能了解new operator和operator new之间的关系。

两个术语表面上令人迷惑,但其实很好理解:

1)如果你希望将对象产生于heap,就是得new operator,它不但分配内存而为该对象调用一个构造函数。

2)如果你只是打算分配内存,请用operator new,就没有构造函数被调用。

3)如果你打算在heap object产生自己决定的内存分配方式,请写一个自己的operator new。并使用new operator,它将会自动调用你所写的operator new。

4)如果你打算在已经分配(并拥有指针)的内存构造对象,请使用placement new 。

3. delete 与内存释放

为了避免resource leaks,每一个动态分配行为都必须匹配一个相应的释放动作。

3.1 函数 operator delete对于内建的delete operator(操作符)就好像 operator new 对于new operator一样。

string *ps;

...

delete ps; //使用delete operator.

内存释放动作是由operator delete执行的,通常声明如下:

void operator delete(void* memoryToBeDeallocated);

因此 delete ps;会造成编译器代码如下:

1)ps->~string();//调用析构函数

2)operator delete(ps);//释放对象所占用的内存

3.2 这里提示我们,如果只打算处理原始的、未设初值的内存,应该完全回避 new operator和delete operator。改为调用operator new取得内存并以operator delete归还系统。

例如:

void* buffer=operator new (50*sizeof(char));//分配内存,放置50个char,没有调用构造函数

...

operator delete(buffer); //释放内存,而没有直接调用析构函数。

这组行为类似malloc和free。

3.3 placement new

如果使用了placement new ,在某块内存中产生对象,你应该避免那块内存使用delete operator(操作符)。

因为delete operator会调用operator delete来释放内存,但是该内存所含的对象最初并不是由operator new 分配来的。placement new只是返回它接收的指针而已,谁知道那个指针从哪里来呢?

所以为了抵消该对象的构造函数的影响,使用placement new 时应该直接调用该对象的析构函数。

例如:

void * mallocShared(size_t size);//申请分配内存

void freeShared(void * momery);//释放内存

void* sharedMemory=mallocShared(sizeof(Widget));

Widget *pw=constructWidgetBuffer(sharedMemory,10);//使用前面Widget类的placement new

...

delete pw;//无定义,因为sharedMemory来自mallocShared,不是来自new。

pw->~Widget();//OK,析构函数pw所指Widget对象,但并未释放Widget所占用内存。

freeShared(pw);//OK,释放pw所指的内存,不调用任何析构函数。

如上述所示,如果交给placement new的原始内存(raw memory)本身是动态分配而得的,那么最终得释放那块内存,以避免memory leak。

4. 动态分配数组(Arrays)

前面所做的都是基于单一对象上的,如果是一组对象呢?

string *ps=new string[10];//分配一个对象数组

4.1 这里的new 与前面的new 行为类似,但略有不同,这里不能再operator new分配内存,而是以operator new[]负责分配

和operator new 一样,operator new[]也可以被重载。

注:operator new[]是相当晚的时候才加入C++的一个特性,所以你的编译器不一定能支持它。如果是这样,全局的operator new 会被用来为每一个数组分配内存(不论数组中的对象是什么类型)。在这样的编译器下定制“数组内存分配行为”很困难,因为你得改写全局的operator new才行。默认情况下,全局版的operator new 负责程序中所有的动态内存分配,所以其行为的任何改变都可能带来全局的影响。

另外,前面讲过,operator new 只允许size_t一个参数。所以你如果决定声明为自己的函数,你的程序便不兼容于任何做了相同决定的程序库。

多方面考虑之下,如果编译器不支持operator new[],定制数组内存管理行为,不是一个明智的决定。

4.2 数组的new 与单一对象的new所调用的构造函数不同,数组的new 必须针对数组中每一个对象调用一个构造函数。

string *ps=new string[10];//调用operator new[]以分配足够容纳10个string对象的内存,然后针对每个元素调用string的默认构造函数。

同样的,当使用了delete,它也会针对数组中每一个元素调用析构函数,然后再调用operator delete[]释放内存。

如:delete []ps;//为数组中的每一个元素调用string 析构函数,然后再调用 operator delete[] 释放内存。 (先调用析构函数,再释放内存。)

跟operator delete一样 operator delete[]也可以被重载。

最后小结一下,new 和delete都是内建的操作符,语言本身所固定了,无法重新定制。但它所调用的内存分配/释放的函数,即operator new和operator delete可以被重载。

转载自:http://www.cnblogs.com/fly1988happy/archive/2012/04/26/2471099.html

随机推荐

  1. Tesseract-OCR引擎 入门

    OCR(Optical Character Recognition):光学字符识别,是指对图片文件中的文字进行分析识别,获取的过程. Tesseract:开源的OCR识别引擎,初期Tesseract引 ...

  2. 用c#写的一个局域网聊天客户端 类似小飞鸽

    用c#写的一个局域网聊天客户端 类似小飞鸽 摘自: http://www.cnblogs.com/yyl8781697/archive/2012/12/07/csharp-socket-udp.htm ...

  3. Erlang 内存泄漏分析

    随着项目越来越依赖Erlang,碰到的问题也随之增加.前段时间线上系统碰到内存高消耗问题,记录一下troubleshooting的分析过程.线上系统用的是Erlang R16B02版本. 问题描述 有 ...

  4. 窗体==>>初始Windows程序

    初识Windows程序 01.创建Windows程序(VS) 01.打开Visual Studio开发工具 02.选择"文件"→"新建"→"项目&qu ...

  5. STL--容器适配器(queue、priority_queue、stack)

    适配器(Adaptor)是提供接口映射的模板类.适配器基于其他类来实现新的功能,成员函数可以被添加.隐藏,也可合并以得到新的功能. STL提供了三个容器适配器:queue.priority_queue ...

  6. 【读书笔记】iOS-GCD-block-后台运行

    当一个app按home键退出的时候,只有最多5秒的时间做一些保存或清理资源的工作.但是调用beginBackgroundTaskWithExpirationHandler方法,可以最多有10分时间在后 ...

  7. 安卓第十四天笔记-内容提供者(ContentProvider)

    安卓第十四天笔记-内容提供者(ContentProvider) ContentProvider--内容提供者 1.ContentProvider简介 ContentProvider是不同应用程序之间进 ...

  8. JAVA基础学习day27--反射机制

    一.概述 1.1.概述 反射的概念: 在Java中的反射机制是指在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法; 对于任意一个对象,都能够调用它的任意一个方法; 这种动态获取信息以及动 ...

  9. 简明 Vim 练级攻略(转)

    原文:http://coolshell.cn/articles/5426.html vim的学习曲线相当的大(参看各种文本编辑器的学习曲线),所以,如果你一开始看到的是一大堆VIM的命令分类,你一定会 ...

  10. C语言-12-日期和时间处理标准库详细解析及示例

    概述 标准库 提供了用于日期和时间处理的结构和函数 是C++语言日期和时间处理的基础 与时间相关的类型 clock_t,本质是:unsigned long typedef unsigned long ...