zoukankan      html  css  js  c++  java
  • C++ new new[]详解

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


    下面是测试代码:
      1 #include<iostream>
      2 void* operator new[](size_t size)
      3 
      4 {
      5 
      6   std::cout<<"call global new[] size: "<<size<<std::endl;
      7 
      8   return malloc(size);
      9 
     10 }
     11 
     12 class Time
     13 
     14 {
     15 
     16 private:
     17 
     18   int hrs,mins,secs;//时,分,秒
     19 
     20 public:
     21 
     22   Time(int hrs=19,int mins=35,int secs=20);//默认参数的带参构造函数
     23 
     24   ~Time();//析构函数
     25 
     26   void showTime()const;
     27 
     28   Time operator ++();//重载前缀递增运算符,++x
     29 
     30   Time operator ++(int);//重载后缀递增运算法,x++
     31 
     32   bool operator ==(const Time &)const;//重载相等性运算符
     33 
     34   Time & operator =(const Time &);//重载赋值运算符
     35 
     36   void * operator new(size_t size);//重载new()运算符,如:int * pInt=new int(0);
     37 
     38   void operator delete(void * ptr);//重载delete()运算符,如:delete pInt;
     39 
     40   void * operator new[](size_t size);//重载new[]()运算符,以分配数组
     41 
     42   void operator delete[](void * ptr);//重载delete[]()运算符,以去配数组,释放数组所占内存
     43 
     44 };
     45 
     46 Time::Time(int hrs,int mins,int secs)
     47 
     48 {
     49 
     50   this->hrs=hrs;
     51 
     52   this->mins=mins;
     53 
     54   this->secs=secs;
     55 
     56   std::cout<<"Time类默认参数的带参构造函数 "<<(this->hrs)<<':'<<(this->mins)<<':'<<(this->secs)<<std::endl;
     57 
     58 }
     59 
     60  
     61 
     62 Time::~Time()
     63 
     64 {
     65 
     66   std::cout<<"Time类析构函数 "<<(this->hrs)<<':'<<(this->mins)<<':'<<(this->secs)<<std::endl;
     67 
     68 }
     69 
     70  
     71 
     72 void Time::showTime()const
     73 
     74 {
     75 
     76   std::cout<<"Time类showTime()const函数 "<<(this->hrs)<<':'<<(this->mins)<<':'<<(this->secs)<<std::endl;
     77 
     78 }
     79 
     80 Time Time::operator ++()//重载前缀递增运算符,++x
     81 
     82 {
     83 
     84   secs++;
     85 
     86   if(secs>=60)
     87 
     88   {
     89 
     90     secs=0;
     91 
     92     mins++;
     93 
     94     if(mins>=60)
     95 
     96     {
     97 
     98       mins=0;
     99 
    100       hrs++;
    101 
    102       if(hrs>=24)
    103 
    104       {
    105 
    106         hrs=0;
    107 
    108       }
    109 
    110     }
    111 
    112   }
    113 
    114   return Time(hrs,mins,secs);//返回无名临时对象
    115 
    116 }
    117 
    118 Time Time::operator ++(int)//重载后缀递增运算法,x++
    119 
    120 {
    121 
    122   Time temp(hrs,mins,secs);//生成临时对象,并进行初始化
    123 
    124   ++secs;
    125 
    126   if(secs>=60)
    127 
    128   {
    129 
    130     secs=0;
    131 
    132     mins++;
    133 
    134     if(mins>=60)
    135 
    136     {
    137 
    138       mins=0;
    139 
    140       hrs++;
    141 
    142       if(hrs>=24)
    143 
    144       {
    145 
    146         hrs=0;
    147 
    148       }
    149 
    150     }
    151 
    152   }
    153 
    154   return temp;
    155 
    156 }
    157 
    158 bool Time::operator ==(const Time & aTime)const//重载相等性运算符
    159 
    160 {
    161 
    162   return ((hrs==aTime.hrs)&&(mins==aTime.mins)&&(secs==aTime.secs));
    163 
    164 }
    165 
    166 Time & Time::operator =(const Time & aTime)//重载赋值运算符
    167 
    168 {
    169 
    170   hrs=aTime.hrs;
    171 
    172   mins=aTime.mins;
    173 
    174   secs=aTime.secs;
    175 
    176   std::cout<<"Time类赋值运算符函数 "<<(this->hrs)<<':'<<(this->mins)<<':'<<(this->secs)<<std::endl;
    177 
    178   return (*this);//返回当前对象的引用
    179 
    180 }
    181 
    182 void * Time::operator new(size_t size)//重载new()运算符,如:int * pInt=new int();
    183 
    184 {
    185 
    186   std::cout<<"operator new() is called.Object size is "<<size<<std::endl;
    187 
    188   return malloc(size);//?//在自由存储中分配内存
    189 
    190 }
    191 
    192 void Time::operator delete(void * ptr)//重载delete()运算符,如:delete pInt;
    193 
    194 {
    195 
    196   std::cout<<"operator delete() is called"<<std::endl;
    197 
    198   free(ptr);//在自由存储中释放内存
    199 
    200 }
    201 
    202 void * Time::operator new[](size_t size)//重载new[]()运算符,以分配数组
    203 
    204 {
    205 
    206   std::cout<<"operator new[]() is called.Object size is "<<size<<std::endl;
    207 
    208   return malloc(size);//?//在自由存储中分配内存
    209 
    210 }
    211 
    212 void Time::operator delete[](void * ptr)//重载delete[]()运算符,以去配数组,释放数组所占内存
    213 
    214 {
    215 
    216   std::cout<<"operator delete[]() is called"<<std::endl;
    217 
    218   free(ptr);//在自由存储中释放内存
    219 
    220 }
    221 
    222  
    223 
    224 int main()
    225 
    226 {
    227 
    228   Time * pTime;
    229 
    230   pTime=new Time;//重载new()运算符,调用默认构造函数
    231 
    232   pTime->showTime();
    233 
    234   delete pTime;//重载delete()运算符
    235 
    236   pTime=new Time[3];//重载new[]()运算符,以分配数组,调用默认构造函数
    237 
    238   delete [] pTime;//重载delete[]()运算符,以去配数组,释放数组所占内存
    239 
    240   getchar();
    241 
    242   return 0;
    243 
    244 }
  • 相关阅读:
    Delphi 简体 繁体 转换
    简单地为DBNavigator填加Caption
    TEdit的 Clear 和 赋值 ''
    SSH服务端
    动态模块导入示例、断言
    异常处理
    反射、getattr
    类的各种自带方法,静态方法,属性方法,类方法等
    类的继承,深度优先于广度优先
    类变量与实例变量、析构函数、私有属性与私有方法
  • 原文地址:https://www.cnblogs.com/wubugui/p/4247725.html
Copyright © 2011-2022 走看看