C++内存管理学习笔记(2) (一)

2014-11-24 00:56:18 · 作者: · 浏览: 2

/****************************************************************/

/* 学习是合作和分享式的!

/* Author:Atlas Email:wdzxl198@163.com


/****************************************************************/

上节内容回顾:传送门

1.C++内存管理

1.1c语言和C++内存分配

1.2区分堆、栈、静态存储区


1.3控制C++的内存分配
在C++中一种常见的问题是对内存的分配,重点是new和delete的使用不当而失控。一般来说,C++对内存的管理非常的容易和安全,当一个对象被消除时,它的析构函数能够安全的释放所分配的内存。所以频繁的使用new和delete动态分配会出现一些问题和堆破碎的风险。

所以,当你必须要使用new 和delete时,你不得不控制C++中的内存分配。你需要用一个全局的new 和delete来代替系统的内存分配符,并且一个类一个类的重载new 和delete。

  一个防止堆破碎的通用方法是从不同固定大小的内存池中分配不同类型的对象。对每个类重载new和delete就提供了这样的控制。

小问:为什么需要重载new和delete?

虽然C++标准库已经为我们提供了new与delete操作符的标准实现,但是由于缺乏对具体对象的具体分析,系统默认提供的分配器在时间和空间两方面都存在着一些问题:分配器速度较慢,而且在分配小型对象时空间浪费比较严重,特别是在一些对效率或内存有较大限制的特殊应用中。比如说在嵌入式的系统中,由于内存限制,频繁地进行不定大小的内存动态分配很可能会引起严重问题,甚至出现堆破碎的风险;再比如在游戏设计中,效率绝对是一个必须要考虑的问题,而标准new与delete操作符的实现却存在着天生的效率缺陷。此时,我们可以求助于new与delete操作符的重载,它们给程序带来更灵活的内存分配控制。除了改善效率,重载new与delete还可能存在以下几点原因:

a)检测代码中的内存错误。

b)性能优化

b)获得内存使用的统计数据。

在《Effective c++》一书中也有讲解为什么需要重载,似乎重载是必须的?对于何种情况下需要重载new和delete的问题,以及如何重载的问题,需要继续研究学习。

详见文章-建议33:小心翼翼地重载operator new/ operator delete

(1)重载全局的new和delete

以下代码为《c++内存管理技术内幕》中是的,只限于简单原理学习

1: void * operator new(size_t size) 2: { 3: void *p = malloc(size); 4: return (p); 5: } 6: void operator delete(void *p) 7: { 8: free(p); 9: }
这段代码可以代替默认的操作符来满足内存分配的请求。出于解释C++的目的,我们也可以直接调用malloc()和free()。

也可以对单个类的new 和 delete 操作符重载。这是你能灵活的控制对象的内存分配。

1: class TestClass { 2: public: 3: void * operator new(size_t size); 4: void operator delete(void *p); 5: // .. other members here ... 6: }; 7: void *TestClass::operator new(size_t size) 8: { 9: void *p = malloc(size); // Replace this with alternative allocator 10: return (p); 11: } 12: void TestClass::operator delete(void *p) 13: { 14: free(p); // Replace this with alternative de-allocator 15: }

所有TestClass 对象的内存分配都采用这段代码。更进一步,任何从TestClass 继承的类也都采用这一方式,除非它自己也重载了new 和 delete 操作符。通过重载new 和 delete 操作符的方法,你可以自由地采用不同的分配策略,从不同的内存池中分配不同的类对象。

(2)为单个类重载new[ ]和delete[ ]

必须小心对象数组的分配。你可能希望调用到被你重载过的new 和 delete 操作符,但并不如此。内存的请求被定向到全局的new[ ]和delete[ ] 操作符,而这些内存来自于系统堆。

C++将对象数组的内存分配作为一个单独的操作,而不同于单个对象的内存分配。为了改变这种方式,你同样需要重载new[ ] 和 delete[ ]操作符。

1: class TestClass { 2: public: 3: void * operator new[ ](size_t size); 4: void operator delete[ ](void *p); 5: // .. other members here .. 6: }; 7: void *TestClass::operator new[ ](size_t size) 8: { 9: void *p = malloc(size); 10: return (p); 11: } 12: void TestClass::operator delete[ ](void *p) 13: { 14: free(p); 15: } 16: int main(void) 17: { 18: TestClass *p = new TestClass[10]; 19: // ... etc ... 20: delete[ ] p; 21: }

但是注意:对于多数C++的实现,new[]操作符中的个数参数是数组的大小加上额外的存储对象数目的一些字节。在你的内存分配机制重要考虑的这一点。你应该尽量避免分配对象数组,从而使你的内存分配策略简单。

另:对于重载new和delete或者new[ ] 和delete[ ],需要考虑诸多事宜,比如错误处理机制,继承、多态等问题。限于篇幅,将在以后的文章中详细讲解,在此买一个伏笔。

(可以参考一篇文章new、delete(new[]、delete[])操作符的重载)。

1.4 内存管理的基本要求
如果只考虑分配和释放,内存管理基本要求是“不重不漏”:既不重复 delete,也不漏掉 delete。也就说我们常说的 new/delete 要配对,“配对”不仅是个数相等,还隐含了 new 和 delete 的调用本身要匹配,不要“东家借的东西西家还”。例如:

用系统默认的 malloc() 分配的内存要交给系统默认的 free() 去释放;
用系统默认的 new 表达式创建的对象要交给系统默认的 delete 表达式去