设为首页 加入收藏

TOP

C++关于new的用法详解(四)
2018-04-19 06:03:57 】 浏览:341
Tags:关于 new 用法 详解
过单步跟踪,很容易发现这4个字节对应的int值为0x00000003,也就是说记录的是我们分配的对象的个数。改变一下分配的个数然后再次观察的结果证实了我的想法。于是,我们也有理由认为new[] operator的行为相当于下面的伪代码:

template <class T>

T* New[](int count)

{

   int size = sizeof(T) * count + 4;

   void* p = T::operator new[](size);

   *(int*)p = count;

   T* pt = (T*)((int)p + 4);

   for(int i = 0; i < count; i++)

       new(&pt[i]) T();

   return pt;

}

上述示意性的代码省略了异常处理的部分,只是展示当我们对一个复杂类型使用new[]来动态分配数组时其真正的行为是什么,从中可以看到它分配了比预期多4个字节的内存并用它来保存对象的个数,然后对于后面每一块空间使用placement new来调用无参构造函数,这也就解释了为什么这种情况下类必须有无参构造函数,最后再将首地址返回。类似的,我们很容易写出相应的delete[]的实现代码:

template <class T>

void Delete[](T* pt)

{

   int count = ((int*)pt)[-1];

   for(int i = 0; i < count; i++)

       pt[i].~T();

   void* p = (void*)((int)pt – 4);

   T::operator delete[](p);

}

由此可见,在默认情况下operator new[]与operator new的行为是相同的,operator delete[]与operator delete也是,不同的是new operator与new[] operator、delete operator与delete[] operator。当然,我们可以根据不同的需要来选择重载带有和不带有“[]”的operator new和delete,以满足不同的具体需求。

把前面类MyClass的代码稍做修改——注释掉析构函数,然后再来看看程序的输出:

calling new[] with size=12 address=003A5A58

ctor

ctor

ctor

address of mc=003A5A58

这一次,new[]老老实实的申请了12个字节的内存,并且申请的结果与new[] operator返回的结果也是相同的,看来,是否在前面添加4个字节,只取决于这个类有没有析构函数,当然,这么说并不确切,正确的说法是这个类是否需要调用构造函数,因为如下两种情况下虽然这个类没声明析构函数,但还是多申请了4个字节:一是这个类中拥有需要调用析构函数的成员,二是这个类继承自需要调用析构函数的类。于是,我们可以递归的定义“需要调用析构函数的类”为以下三种情况之一:

1 显式的声明了析构函数的

2 拥有需要调用析构函数的类的成员的

3 继承自需要调用析构函数的类的

类似的,动态申请简单类型的数组时,也不会多申请4个字节。于是在这两种情况下,释放内存时使用delete或delete[]都可以,但为养成良好的习惯,我们还是应该注意只要是动态分配的数组,释放时就使用delete[]。

释放内存时如何知道长度

但这同时又带来了新问题,既然申请无需调用析构函数的类或简单类型的数组时并没有记录个数信息,那么operator delete,或更直接的说free()是如何来回收这块内存的呢?这就要研究malloc()返回的内存的结构了。与new[]类似的是,实际上在malloc()申请内存时也多申请了数个字节的内容,只不过这与所申请的变量的类型没有任何关系,我们从调用malloc时所传入的参数也可以理解这一点——它只接收了要申请的内存的长度,并不关系这块内存用来保存什么类型。下面运行这样一段代码做个实验:

char *p = 0;

for(int i = 0; i < 40; i += 4)

{

   char* s = new char[i];

   printf("alloc %2d bytes, address=%p distance=%dn", i, s, s - p);

   p = s;

}

我们直接来看VC2005下Release版本的运行结果,DEBUG版因包含了较多的调试信息,这里就不分析了:

alloc 0 bytes, address=003A36F0 distance=3815152

alloc 4 bytes, address=003A3700 distance=16

alloc 8 bytes, address=003A3710 distance=16

alloc 12 bytes, address=003A3720 distance=16

alloc 16 bytes, address=003A3738 distance=24

alloc 20 bytes, address=003A84C0 distance=19848

alloc 24 bytes, address=003A84E0 distance=32

alloc 28 bytes, address=003A8500 distance=32

alloc 32 bytes, address=003A8528 distance=40

alloc 36 bytes, address=003A8550 distance=40

每一次分配的字节数都比上一次多4,distance值记录着与上一次分配的差值,第一个差值没有实际意义,中间有一个较大的差值,可能是这块内存已经被分配了,于是也忽略它。结果中最小的差值为16字节,直到我们申请16字节时,这个差值变成了24,后面也有类似的规律,那么我们可以认为申请所得的内存结构是如下这样的:

\

从图中不难看出,当我们要分配一段内存时,所得的内存地址和上一次的尾地址至少要相距8个字节(在DEBUG版中还要更多),那么我们可以猜想,这8个字节中应该记录着与这段所分配的内存有关的信息。观察这8个节内的内容,得到结果如下:

图中右边为每次分配所得的地址之前8个字节的内容的16进制表示,从图中红线所表示可以看到,这8个字节中的第一个字节乘以8即得到相临两次分配时的距离,经过试验一次性分配更大的长度可知,第二个字节也是这个意义,并且代表高8位,也就说前面空的这8个字节中的前两个字节记录了一次分配内存的长度信息,后面的六个字节可能与空闲内存链表的信息有关,在翻译内存时用来提供必要的信息。这就解答了前面提出的问题,原来C/C++在分配内存时已经记录了足够充分的信息用于回收内存,只不过我们平常不关心它罢了。

首页 上一页 1 2 3 4 下一页 尾页 4/4/4
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇C++基础——友元类和友元的关系性.. 下一篇C++中explicit关键字的作用是什么..

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目