C++中向系统申请堆内存的方法为使用new、new[]操作符,new申请单个对象的内存,new[]申请对象数组的内存。对应的delete、delete[]操作符将new、new[]操作符申请到的内存还给系统。使用new运算符的表达式被称为new表达式,相应的,使用delete运算符的表达式被称为delete表达式。

C++默认的new表达式有3种形式。

(1).最基本也是最常见的,完成内存空间申请并调用构造函数

new type

new type(initilalizers)

new type[size]

new type[size]{braced initializer list}

(2).指定不抛异常的,完成内存空间申请并调用构造函数

new(nothrow) type

new(nothrow) type(initializers)

new(nothrow) type[size]

new(nothrow) type[size]{braced initializer list}

(3).只创建对象不分配内存的

new(place_address) type

new(place_address) type(initializers)

new(place_address) type[size]

new(place_address) type[size]{braced initializer list}

而C++默认的delete表达式只有一种形式

delete   ptr

delete[] ptr

以单对象内存的分配为例:

new表达式1的工作原理如下:1.调用 void * operator new(size_t)函数分配内存 2.在第1步返回的内存上调用对象的构造函数 3.返回内存指针。

new表达式1在第1步和第2步都是可能抛出异常的。在第1步,如果系统没有足够内存可分配,void * operator new(size_t)函数会抛出 bad_alloc异常。在第2步,如果对象的构造函数抛出了异常,为了防止内存泄露,编译器会将异常捕获,然后调用 void operator delete(void *)noexcept函数回收内存,最后将异常再次抛出。

new表达式2的工作原理如下:1.调用 void *operator new(size_t ,const std::nothrow_t &)noexcept函数分配内存 2.在第1步返回的内存上调用对象的构造函数 3.返回内存指针。

new表达式2的第1步在分配不到内存的情况下不会抛出异常,而是返回空指针。在第2步,如果对象的构造函数抛出了异常,为了防止内存泄露,编译器也会将异常捕获,然后回收内存,但调用的函数是 void operator delete(void *,const std::nothrow_t &)noexcept。

new表达式3的工作原理如下:1.调用 void *operator new(size_t,void *)noexcept返回内存 2.在第1步返回的内存上调用对象的构造函数 3.返回内存指针。

new表达式3的第1步调用的 void *operator new(size_t,void *)noexcept不向系统申请内存,只是把第二个参数返回。这样,第2步对象的构造就是在用户指定的内存上进行的。第2步跟上面两个一样,也是对对象进行构造,但在构造函数抛出异常的情况下,回收内存时调用的函数为 void operator delete(void *,void *)noexcept,这个函数啥都不干。

new表达式2、3又被称为定位new表达式。

delete 表达式的工作原理如下:1.调用对象的析构函数 2.调用 void operator delete(void *)回收内存。

如果是数组版本的new表达式、delete表达式,以上函数的对应版本为

void * operator new(size_t) 对应 void * operator new[](size_t)

void * operator new(size_t,const std::nothrow_t &)noexcept 对应 void * operator new[](size_t,const std::nothrow_t &)noexcept

void * operator new(size_t,void *)noexcept 对应 void * operator new[](size_t,void *)noexcept

void operator delete(void *)noexcept 对应 void operator delete[](void *)noexcept

void operator delete(void *,const std::nothrow_t &)noexcept对应 void operator delete[](void *,const std::nothrow_t &)noexcept

void operator delete(void *,void *)noexcept 对应 void operator delete[](void *,void *)noexcept

这以上12个函数都位于 标准库<new>中,除了 void *operator new(size_t,void *)这一类的四个函数外,其它8个函数都可以被用户重定义。

重定义途径有两种,第一种是在全局作用域中重新定义,第二种是在类作用域中重新定义。在namespace中定义这8个函数是错误的。如果是一般函数,第一种途径是会引发重定义错误的,但C++标准为new、delete开了后门。new、delete表达式在寻找实际应该使用哪个函数的顺序是这样的:如果是对象是类类型,则先在该类以及该类的基类的成员函数中寻找相应的函数,如果没有,则到全局作用域中去寻找。如果全局作用域中有用户自定义的版本,则使用用户自定义版本。如果没有用户自定义版本,则使用标准库的版本。

如果在类作用域中重载这8个函数,则它们是static的,即使没有用static来声明。

operator new系列函数与operator new[]系列函数的功能都是分配n个字节的内存,那它们没有区别吗?没有区别。所以标准库中operator new[]是直接调用的operator new来工作的,要自定义分配内存函数时只用自定义operator new就可以了。虽然new表达式与new[]表达式是有区别的。new表达式分配单个对象,n不可能为0。而在C++中new type[0]是合法的,而且必须返回非空指针(但该指针不能解引用)。所以new[]表达式要处理n==0的情况。而且,delete[]表达式是要对对象进行析构的,但delete[]表达式是如何知道有几个对象的?所以new[]在申请内存的时候要申请额外的内存来存储它申请的元素的个数,delete[]的时候按照规则来读就行了。这两点区别被编译器解决了,对用户是透明的。operator new、operator new[]只申请内存就可以。

另外,标准库中有 new_handler set_new_handler(new_handler new_p)noexcept 函数,可用于指定标准库的operator new 、operator new[]函数在申请不到内存的情况下的行为。对抛异常版本与不抛异常版本都有效。因为new_handler会被循环调用直到申请到内存或者new_handler抛异常。为了不让程序在极端情况下一直死循环下去,new_handler必须抛异常或exit退出程序来结束循环,但抛异常会导致 new(nothrow)也抛异常,破坏new(nothrow)不抛异常的约定。所以如果自定义new_handler,退出循环只有调用exit一条路可走了?

new_handler对用户自定义的版本无效。

get_new_handler可用于获取用户自定义的new_handler,如果用户无自定义,返回nullptr。

知道了以上的知识点能做什么呢?

1.自定义operator new函数,控制内存分配行为。

2.分离堆内存分配与对象创建,提高程序效率。

关于这第二点,C++标准库还提供了其它设施来实现同样的目的:

1.template <class T> class allocator;

point allocate(size_t) 分配内存

void deallocate(point,size_t) 回收内存

template <class U,class... Args> void construct(U* p,Args&&... args) 创建对象

template<class U> void destroy(U* p) 销毁对象

  allocator 分配内存实际上调用的是::operator new,所以如果用户在全局作用域自定义了operator new,也会影响allocator。

2.template <class T>   pair<T *,ptrdiff_t> get_temporary_buffer(ptrdiff_t n) noexcept

该函数试图分配 够存储n个元素的空间,但有可能最后分配的小于n。

该函数语义上被限制于只分配临时空间,即当局部变量使用。该函数的实现上应该是从栈上(或编译器自已维护的一段内存)分配空间,而不是从堆上。所以自定义operator new对其无影响。因为是从栈上分配空间,所以其能分配的空间比较小,但会比 ::operator new快。

template <class T> void return_temporary_buffer(T *p);用于归还 get_temporary_buffer分配的内存。注意它并不销毁对象,需要用户自己销毁。

标准库 allocator 与new、delete的区别

  new 与 new[]是不同的,上面已经说明过(new[]需要管理对象个数).

  allocator则只有一种形式,不区分分配单个对象还是多个对象,但对象个数由用户来管理,以便正确destroy.

C++ new、delete的更多相关文章

  1. sql之truncate 、delete与drop区别

    sql之truncate .delete与drop区别相同点:truncate 和不带 where 子句的 delete,以及 drop 一定会删除表内的数据不同点:1. truncate 和 del ...

  2. MySQL数据库INSERT、UPDATE、DELETE以及REPLACE语句的用法详解

    本篇文章是对MySQL数据库INSERT.UPDATE.DELETE以及REPLACE语句的用法进行了详细的分析介绍,需要的朋友参考下   MySQL数据库insert和update语句引:用于操作数 ...

  3. Truncate table、Delete与Drop table的区别

    Truncate table.Delete与Drop table的区别 TRUNCATE TABLE 在功能上与不带 WHERE 子句的 DELETE 语句相同:二者均删除表中的全部行.但 TRUNC ...

  4. WebApi接口传参不再困惑(4):传参详解 一、get请求 二、post请求 三、put请求 四、delete请求 五、总结

    前言:还记得刚使用WebApi那会儿,被它的传参机制折腾了好久,查阅了半天资料.如今,使用WebApi也有段时间了,今天就记录下API接口传参的一些方式方法,算是一个笔记,也希望能帮初学者少走弯路.本 ...

  5. 操作数据(insert、update、delete)

    插入数据 使用Insert Into 插入 if(exists(select * from sys.databases where name = 'webDB')) drop database web ...

  6. sql中truncate 、delete与drop区别

    SQL truncate .delete与drop区别   相同点: 1.truncate和不带where子句的delete.以及drop都会删除表内的数据. 2.drop.truncate都是DDL ...

  7. oracle中drop、delete和truncate的区别

    oracle中drop.delete和truncate的区别 oracle中可以使用drop.delete和truncate三个命令来删除数据库中的表,网上有许多文章和教程专门讲解了它们之间的异同,我 ...

  8. The use of function Merge (update、insert、delete)

    1.The use of function merge(update.insert.delete) Example: #1.Initialize the data create table #test ...

  9. SQL Server DML(UPDATE、INSERT、DELETE)常见用法(一)

    1.引言 T-SQL(Transact Structured Query Language)是标准的SQL的扩展,是程序和SQL Server沟通的主要语言. T-SQL语言主要由以下几部分组成: 数 ...

随机推荐

  1. DSP5509的时钟发生器(翻译总结自TI官方文档)

    一.C5509时钟发生器的两个功能 1.将从CLKIN引脚输入的时钟信号变换为适当频率的CPU时钟,提供给CPU.外设和其他模块使用: 2.将CPU时钟通过可编程分频器输出到CLKOUT引脚. 时钟发 ...

  2. .NET设计模式(12):外观模式(Façade Pattern)(转)

    概述 在软件开发系统中,客户程序经常会与复杂系统的内部子系统之间产生耦合,而导致客户程序随着子系统的变化而变化.那么如何简化客户程序与子系统之间的交互接口?如何将复杂系统的内部子系统与客户程序之间的依 ...

  3. 你用什么工具开发JavaScript?

    Cloud9的CFO曾经在LinkedIn上面发起一个调查,原标题为:What tools do you use for Node.js development?,回贴者甚多. 目测很多人都使用VIM ...

  4. python笔记1

    1.python中的语句块是用缩进表示,并不像C类语言中用{}表示语句块,还有就是语句块的开始貌似是用:表示,然后C类语言中()在python中用"空格"表示了,例如python中 ...

  5. Twitter注册

    Twitter注册 - (一般分享不了是回调地址不对) 1.打开twitter的官网https://dev.twitter.com,如果还没有注册账号的,需要注册账号,已经注册账号的,请先登录: 2. ...

  6. ExtJS之Ext.getDom

    <!DOCTYPE html> <html> <head> <title>ExtJs</title> <meta http-equiv ...

  7. 被 Windows 10 SDK 坑了

    抓包抓到的配置文件与完整版的VS一致,虽然显示出来的只有 Windows 10 通用工具,我还以为是只更新这些(因为列出来的几个,我确实之前没装),就愉快地点了安装,结果……新建项目时,发现 Wind ...

  8. mp3 音频 音乐 tag ID3 ID3V1 ID3V2 标签 读取信息 获得图片 jpeg bmp 图片转换等

    mp3 音频 音乐 tag ID3 ID3V1 ID3V2 标签 读取信息 获得图片 jpeg bmp 图片转换(上) MP3文件格式(二)---ID3v2 图:ID3V1标签结构 图:ID3V2标签 ...

  9. Java-J2SE学习笔记-线程-生产者消费者问题

    一.概述 模拟生产者消费者问题 二.代码 1.Consumer.java 2.Producer.java 3.SyncStack.java 4.Test.java 1.Consumer.java pa ...

  10. C++不能显式调用构造函数,会生成匿名对象,这点与Java完全不一样!

    Java可以直接调用同名构造函数,仅仅起初始化的功能,并不构造新的对象,但是C++里面没有.看一下这段代码: class A { public: A() { printf("A() \n&q ...