精髓:
operator new()完成的操作一般只是分配内存;而构造函数的调用(如果需要)是在new运算符中完成的。
operator new和new 运算符是不同的,operator new只分配内存,而只要new出现无论是不是operator new都会调用new运算符从而调用析构函数。

例子是:

#ifndef __PLACEMENT_NEW_INLINE  

#define __PLACEMENT_NEW_INLINE  

inline void *__cdecl operator new(size_t, void *_P)  

        {return (_P); }  

#if     _MSC_VER >= 1200  

inline void __cdecl operator delete(void *, void *)  

    {return; }  

#endif

#endif
这是new.h下的源代码,可见这个placement new(一种operator new)只是简单的返回了地址而已,并没有调用构造函数,这就说明,构造函数是只要出现new就必定会调用的事实。
我们可以使用如下技术来在制定的内存上调用构造:
  1. A* s = new(p) A(XXX);
注意:这个new是placement new不分配内存,仅仅只call new运算符调用构造函数。

详解:

A* a = new A;

 我们知道这里分为两步:1.分配内存,2.调用A()构造对象.
事实上,分配内存这一操作就是由operator new(size_t)来完成的,如果类A重载了operator new,那么将调用A::operator new(size_t ),如果没有重载,就调用::operator new(size_t ),全局new操作符由C++默认提供。

operator new的三种形式:

operator new有三种形式:
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();
(1)(2)的区别仅是是否抛出异常,当分配失败时,前者会抛出bad_alloc异常,后者返回null,不会抛出异常。它们都分配一个固定大小的连续内存。
用法示例:
A* a = new A; //调用throwing(1)
A* a = new(std::nothrow) A; //调用nothrow(2)
(3)是placement new,它也是对operator new的一个重载,定义于<new>中,它多接收一个ptr参数,但它只是简单地返回ptr。
它可以实现在ptr所指地址上构建一个对象(通过调用其构造函数),这在内存池技术上有广泛应用。
new(p) A();// 这个操作实际上是先调用了 operator new,这个operator new就是placement new(注意placement new不会分配内存),然后在p处调用了构造函数
前面说到,new运算符都会调用operator new,而这里的operator new(size_t, void*)并没有什么作用,真正起作用的是new运算符的第二个步骤:在p处调用A构造函数。这里的p可以是动态分配的内存,也可以是栈中缓冲,如char buf[100]; new(buf) A();
placement new的主要作用只是将p放入ecx,并且调用其构造函数。

c++为什么定义了析构函数的类的operator new[]传入的参数会多4字节?
class A

{

public:

A()

{

std::cout<<"call A constructor"<<std::endl;

}

~A()

{

std::cout<<"call A destructor"<<std::endl;

}

void* operator new(size_t size)

{

std::cout<<"call A::operator new[] size:"<<size<<std::endl;

return malloc(size);

}

void operator delete[](void* p)

{

std::cout<<"call A::operator delete[]"<<std::endl;

free(p);

}

void operator delete(void* p)

{

free(p);

}

};

//cpp
#include <iostream>
#include "A.h" void* operator new[](size_t size) { std::cout<<"call global new[] size: "<<size<<std::endl; return malloc(size); } void operator delete[](void* p) { std::cout<<"call global delete[] "<<std::endl; } int _tmain(int argc, _TCHAR* argv[]) { std::cout<<"sizeof A "<<sizeof(A)<<std::endl; A* p1 = new A[]; delete []p1; system("pause"); return ; }
注意:只要在类中重载了new[]运算,就可以使用new A[n]来动态new数组,并且按照自定义的new方式来分配内存。但是他不会去调用自定义的new因为这两个运算符没有关联。实现的时候应该像如下实现:
 void * Time::operator new[](size_t size)//重载new[]()运算符,以分配数组
{ std::cout<<"operator new[]() is called.Object size is "<<size<<std::endl; return malloc(size);//?//在自由存储中分配内存 }
使用malloc连续分配数组需要的所有内存,而不是一个一个的来分配。即这个size等于一数组对象的所需要的内存量。
注意:如果所分配的对象显示定义了析构函数,那么size会比对象个数X对象占用内存4字节+对齐字节数,这里暂时不考虑字节对齐数,只考虑那四字节:
这四个字节是用来记录数组长度的,方便在delete的时候(这里不用在delete重载中显示写出来,因为delete包含了一种运算符属性,只要出现delete有析构,必定调用析构)对每一个对象调用一次析构。要确定对多大的内存块进行析构就是下面的算式决定:
  1. (size - 4)/(那多出来四字节的值)
很明显,那4字节的值等于调用析构函数的次数。
如果没有显示定义析构的话,就不会多那4字节,在删除那段申请出来的连续内存时,由于不用调用析构直接全部抹掉即可。
注意:不同的平台上编译器的实现都是不同的,所以是不是有那4字节都不一定。


下面是测试代码:
 #include<iostream>
void* operator new[](size_t size) {   std::cout<<"call global new[] size: "<<size<<std::endl;   return malloc(size); } class Time { private:   int hrs,mins,secs;//时,分,秒 public:   Time(int hrs=,int mins=,int secs=);//默认参数的带参构造函数   ~Time();//析构函数   void showTime()const;   Time operator ++();//重载前缀递增运算符,++x   Time operator ++(int);//重载后缀递增运算法,x++   bool operator ==(const Time &)const;//重载相等性运算符   Time & operator =(const Time &);//重载赋值运算符   void * operator new(size_t size);//重载new()运算符,如:int * pInt=new int(0);   void operator delete(void * ptr);//重载delete()运算符,如:delete pInt;   void * operator new[](size_t size);//重载new[]()运算符,以分配数组   void operator delete[](void * ptr);//重载delete[]()运算符,以去配数组,释放数组所占内存 }; Time::Time(int hrs,int mins,int secs) {   this->hrs=hrs;   this->mins=mins;   this->secs=secs;   std::cout<<"Time类默认参数的带参构造函数 "<<(this->hrs)<<':'<<(this->mins)<<':'<<(this->secs)<<std::endl; } Time::~Time() {   std::cout<<"Time类析构函数 "<<(this->hrs)<<':'<<(this->mins)<<':'<<(this->secs)<<std::endl; } void Time::showTime()const {   std::cout<<"Time类showTime()const函数 "<<(this->hrs)<<':'<<(this->mins)<<':'<<(this->secs)<<std::endl; } Time Time::operator ++()//重载前缀递增运算符,++x {   secs++;   if(secs>=)   {     secs=;     mins++;     if(mins>=)     {       mins=;       hrs++;       if(hrs>=)       {         hrs=;       }     }   }   return Time(hrs,mins,secs);//返回无名临时对象 } Time Time::operator ++(int)//重载后缀递增运算法,x++ {   Time temp(hrs,mins,secs);//生成临时对象,并进行初始化   ++secs;   if(secs>=)   {     secs=;     mins++;     if(mins>=)     {       mins=;       hrs++;       if(hrs>=)       {         hrs=;       }     }   }   return temp; } bool Time::operator ==(const Time & aTime)const//重载相等性运算符 {   return ((hrs==aTime.hrs)&&(mins==aTime.mins)&&(secs==aTime.secs)); } Time & Time::operator =(const Time & aTime)//重载赋值运算符 {   hrs=aTime.hrs;   mins=aTime.mins;   secs=aTime.secs;   std::cout<<"Time类赋值运算符函数 "<<(this->hrs)<<':'<<(this->mins)<<':'<<(this->secs)<<std::endl;   return (*this);//返回当前对象的引用 } void * Time::operator new(size_t size)//重载new()运算符,如:int * pInt=new int(); {   std::cout<<"operator new() is called.Object size is "<<size<<std::endl;   return malloc(size);//?//在自由存储中分配内存 } void Time::operator delete(void * ptr)//重载delete()运算符,如:delete pInt; {   std::cout<<"operator delete() is called"<<std::endl;   free(ptr);//在自由存储中释放内存 } void * Time::operator new[](size_t size)//重载new[]()运算符,以分配数组 {   std::cout<<"operator new[]() is called.Object size is "<<size<<std::endl;   return malloc(size);//?//在自由存储中分配内存 } void Time::operator delete[](void * ptr)//重载delete[]()运算符,以去配数组,释放数组所占内存 {   std::cout<<"operator delete[]() is called"<<std::endl;   free(ptr);//在自由存储中释放内存 } int main() {   Time * pTime;   pTime=new Time;//重载new()运算符,调用默认构造函数   pTime->showTime();   delete pTime;//重载delete()运算符   pTime=new Time[];//重载new[]()运算符,以分配数组,调用默认构造函数   delete [] pTime;//重载delete[]()运算符,以去配数组,释放数组所占内存   getchar();   return ; }

C++ new new[]详解的更多相关文章

  1. Linq之旅:Linq入门详解(Linq to Objects)

    示例代码下载:Linq之旅:Linq入门详解(Linq to Objects) 本博文详细介绍 .NET 3.5 中引入的重要功能:Language Integrated Query(LINQ,语言集 ...

  2. 架构设计:远程调用服务架构设计及zookeeper技术详解(下篇)

    一.下篇开头的废话 终于开写下篇了,这也是我写远程调用框架的第三篇文章,前两篇都被博客园作为[编辑推荐]的文章,很兴奋哦,嘿嘿~~~~,本人是个很臭美的人,一定得要截图为证: 今天是2014年的第一天 ...

  3. EntityFramework Core 1.1 Add、Attach、Update、Remove方法如何高效使用详解

    前言 我比较喜欢安静,大概和我喜欢研究和琢磨技术原因相关吧,刚好到了元旦节,这几天可以好好学习下EF Core,同时在项目当中用到EF Core,借此机会给予比较深入的理解,这里我们只讲解和EF 6. ...

  4. Java 字符串格式化详解

    Java 字符串格式化详解 版权声明:本文为博主原创文章,未经博主允许不得转载. 微博:厉圣杰 文中如有纰漏,欢迎大家留言指出. 在 Java 的 String 类中,可以使用 format() 方法 ...

  5. Android Notification 详解(一)——基本操作

    Android Notification 详解(一)--基本操作 版权声明:本文为博主原创文章,未经博主允许不得转载. 微博:厉圣杰 源码:AndroidDemo/Notification 文中如有纰 ...

  6. Android Notification 详解——基本操作

    Android Notification 详解 版权声明:本文为博主原创文章,未经博主允许不得转载. 前几天项目中有用到 Android 通知相关的内容,索性把 Android Notificatio ...

  7. Git初探--笔记整理和Git命令详解

    几个重要的概念 首先先明确几个概念: WorkPlace : 工作区 Index: 暂存区 Repository: 本地仓库/版本库 Remote: 远程仓库 当在Remote(如Github)上面c ...

  8. Drawable实战解析:Android XML shape 标签使用详解(apk瘦身,减少内存好帮手)

    Android XML shape 标签使用详解   一个android开发者肯定懂得使用 xml 定义一个 Drawable,比如定义一个 rect 或者 circle 作为一个 View 的背景. ...

  9. Node.js npm 详解

    一.npm简介 安装npm请阅读我之前的文章Hello Node中npm安装那一部分,不过只介绍了linux平台,如果是其它平台,有前辈写了更加详细的介绍. npm的全称:Node Package M ...

  10. .NET应用和AEAI CAS集成详解

    1 概述 数通畅联某综合SOA集成项目的统一身份认证工作,需要第三方系统配合进行单点登录的配置改造,在项目中有需要进行单点登录配置的.NET应用系统,本文专门记录.NET应用和AEAI CAS的集成过 ...

随机推荐

  1. classloader 学习

    classloader就是把类文件加载到jvm中供虚拟机使用,先看一个magic小例子: 首先,我定义一个alex/vicky包,然后在这个包内定义一个接口: public interfaceISer ...

  2. CENTOS 7 install mariadb 10.3

    CENTOS install mariadb 10.3 cat >/etc/yum.repos.d/MariaDB.repo << 'EOF' [mariadb] name = Ma ...

  3. 转 深入解析:一主多备DG环境,failover的实现过程详解 以及 11g 容灾库可以在线添加tempfile. 以及 11g 容灾库可以在线添加logile.

    https://yq.aliyun.com/articles/229600 核心,就是11g通过datafille_scn 号来追日志,而不是日志序列号来追日志. 加快standby switchov ...

  4. 判断元素类型JS

    <!doctype html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  5. jenkins自动打IOS包(转发)

    投稿文章,作者:一缕殇流化隐半边冰霜(@halfrost) 前言 众所周知,现在App的竞争已经到了用户体验为王,质量为上的白热化阶段.用户们都是很挑剔的.如果一个公司的推广团队好不容易砸了重金推广了 ...

  6. SolrCloud的搭建的连接

    1 什么是SolrCloud SolrCloud(solr 云)是Solr提供的分布式搜索方案,当你需要大规模,容错,分布式索引和检索能力时使用SolrCloud.当一个系统的索引数据量少的时候是不需 ...

  7. eclipse编码转换

    一般Java文件编码格式是UTF-8的.以下以默认GBK改为UTF-8为例. 1.改变整个工作空间的编码格式,这样以后新建的文件也是新设置的编码格式.     eclipse->window-& ...

  8. 先定个小目标, 使用C# 开发的千万级应用

    dotNET跨平台 微信号 opendotnet 功能介绍 在这里你可以谈微软.NET,Mono的跨平台开发技术,也可以谈谈其他的跨平台技术.在这里可以让你的.NET项目有新的思路,不局限于微软的技术 ...

  9. iOS 上滑隐藏导航,下滑显示导航,仿斗鱼导航效果

    UItableView或 UIcollectionView 都是继承UIScrollView 滑动的时候,判断是上滑还是下滑 使用 UIScrollView 的代理方法 func scrollView ...

  10. kafka存储机制以及offset

    1.前言 一个商业化消息队列的性能好坏,其文件存储机制设计是衡量一个消息队列服务技术水平和最关键指标之一.下面将从Kafka文件存储机制和物理结构角度,分析Kafka是如何实现高效文件存储,及实际应用 ...