编写高质量代码之改善C++程序语法篇3<内存管理 >
2014-11-25 22:43
603 查看
阅读《编写高质量代码--改善C++程序的150个建议》,总结归纳;
此文用以加深记忆,督促学习的目的
在线阅读http://book.51cto.com/art/201202/317549.htm
0 区分内存分配的方式
内存管理的水平是衡量高手和菜鸟的一个不成为的约定
一个程序加载到计算机内存里,可形成一个运行空间,大致分为:代码区、数据区、堆区、栈区
* 代码区:存放的是程序的执行代码
* 数据区:存放的是全局数据、常量、静态变量等
* 堆区:存放的是动态内存,供程序使用
* 栈区:存放的是程序中所用到的局部数据
+ 代码区是我们不能在代码中控制的,其他三个区是编码过程中利用的
+ 数据区自由存储区、全局/静态存储区和常量存储区
+ 自由存储区:是有malloc等分配的内存块,需要使用free来释放
+ 全局/静态存储区: 全局变量和静态变量被分配到同一块内存中
+ 常量存储区: 存放常量,不允许修改
new/delete 与new[]/delete[]必须配对使用
* delete删除对象时,编译器只会将指针所指向的对象当作单个对象来处理
1 区分new的三种形态
* new operator
这是我们使用最多的
* operator new
* placement new
placement new是用来实现定位构造的,可以通过它来选择合适的构造函数。虽然通常情况下,构造函数是由编译器自动调用的,但是不排除你有时确实想直接手动调用,比如对未初始化的内存进行处理,获取想要的对象,此时就得求助于一个叫做placement
new的特殊的operator new了
总结一下:
如果是在堆上建立对象,那么应该使用 new operator,它会为你提供最为周全的服务。
如果仅仅是分配内存,那么应该调用operator new,但初始化不在它的工作职责之内。如果你对默认的内存分配过程不满意,想单独定制,重载operator new 是不二选择。
如果想在一块已经获得的内存里建立一个对象,那就应该用placement new。但是通常情况下不建议使用,除非是在某些对时间要求非常高的应用中,因为相对于其他两个步骤,选择合适的构造函数完成对象初始化是一个时间相对较长的过程
2 new内存失败后的正确处理
当使用new申请一块内存失败时,抛出异常std::bad_alloc是C++标准中规定的标准行为,所以推荐使用try{ p = new int[SIZE]; } catch( std::bad_alloc ) { ... }的处理方式。但是在一些老旧的编译器中,却不支持该标准,它会返回NULL,此时具有C传统的Test_for_NULL代码形式便起了作用。所以,要针对不同的情形采取合理的处置方式。
3 了解new_handler的所作所为
通常,一个好的new-handler函数的处理方式必须遵循以下策略之一:
Deinstall the new-handler(卸载new-handler)
Make more memory available(使更大块内存有效)
Install a different new-handler(装载另外的new-handler)
Throw an exception(抛出异常)
Not return(无返回)
直接调用abort或exit结束应用程序。
4 借助工具检测内存泄露问题
* linux下的Valgrind和Rational Purify
5 小心翼翼的重载operator new/ operator delete
6 用智能指针管理通过new创建的对象
7 使用内存池技术提高内存申请效率和性能
经典的内存池实现原理如下:
Boost库同样对该技术提供了较好的支持:
pool的析构函数会释放pool占用的内存。
此文用以加深记忆,督促学习的目的
在线阅读http://book.51cto.com/art/201202/317549.htm
0 区分内存分配的方式
内存管理的水平是衡量高手和菜鸟的一个不成为的约定
一个程序加载到计算机内存里,可形成一个运行空间,大致分为:代码区、数据区、堆区、栈区
* 代码区:存放的是程序的执行代码
* 数据区:存放的是全局数据、常量、静态变量等
* 堆区:存放的是动态内存,供程序使用
* 栈区:存放的是程序中所用到的局部数据
+ 代码区是我们不能在代码中控制的,其他三个区是编码过程中利用的
+ 数据区自由存储区、全局/静态存储区和常量存储区
+ 自由存储区:是有malloc等分配的内存块,需要使用free来释放
+ 全局/静态存储区: 全局变量和静态变量被分配到同一块内存中
+ 常量存储区: 存放常量,不允许修改
new/delete 与new[]/delete[]必须配对使用
* delete删除对象时,编译器只会将指针所指向的对象当作单个对象来处理
1 区分new的三种形态
* new operator
这是我们使用最多的
string *pStr = new string("Memory Management"); int *pInt = new int(2011);
* operator new
class A { public: A(int a); ~A(); void* operator new(size_t size); ... }; void* A::operator new(size_t size) { cout<<"Our operator new..."); return ::operator new(size); }
* placement new
placement new是用来实现定位构造的,可以通过它来选择合适的构造函数。虽然通常情况下,构造函数是由编译器自动调用的,但是不排除你有时确实想直接手动调用,比如对未初始化的内存进行处理,获取想要的对象,此时就得求助于一个叫做placement
new的特殊的operator new了
#include <new> #include "ClassA.h" int main() { void *s = operator new(sizeof(A)); A* p = (A*)s; new(p) A(2011); //p->A::A(2011); ... // processing code return 0; }
总结一下:
如果是在堆上建立对象,那么应该使用 new operator,它会为你提供最为周全的服务。
如果仅仅是分配内存,那么应该调用operator new,但初始化不在它的工作职责之内。如果你对默认的内存分配过程不满意,想单独定制,重载operator new 是不二选择。
如果想在一块已经获得的内存里建立一个对象,那就应该用placement new。但是通常情况下不建议使用,除非是在某些对时间要求非常高的应用中,因为相对于其他两个步骤,选择合适的构造函数完成对象初始化是一个时间相对较长的过程
2 new内存失败后的正确处理
当使用new申请一块内存失败时,抛出异常std::bad_alloc是C++标准中规定的标准行为,所以推荐使用try{ p = new int[SIZE]; } catch( std::bad_alloc ) { ... }的处理方式。但是在一些老旧的编译器中,却不支持该标准,它会返回NULL,此时具有C传统的Test_for_NULL代码形式便起了作用。所以,要针对不同的情形采取合理的处置方式。
3 了解new_handler的所作所为
通常,一个好的new-handler函数的处理方式必须遵循以下策略之一:
Deinstall the new-handler(卸载new-handler)
Make more memory available(使更大块内存有效)
Install a different new-handler(装载另外的new-handler)
Throw an exception(抛出异常)
Not return(无返回)
直接调用abort或exit结束应用程序。
4 借助工具检测内存泄露问题
* linux下的Valgrind和Rational Purify
5 小心翼翼的重载operator new/ operator delete
6 用智能指针管理通过new创建的对象
7 使用内存池技术提高内存申请效率和性能
经典的内存池实现原理如下:
class MemPool { public: MemPool(int nItemSize, int nMemBlockSize = 2048) : m_nItemSize(nItemSize), m_nMemBlockSize(nMemBlockSize), m_pMemBlockHeader(NULL), m_pFreeNodeHeader(NULL) { } ~ MemPool(); void* Alloc(); void Free(); private: const int m_nMemBlockSize; const int m_nItemSize; struct _FreeNode { _FreeNode* pPrev; BYTE data[m_nItemSize - sizeof(_FreeNode*)]; }; struct _MemBlock { _MemBlock* pPrev; _FreeNode data[m_nMemBlockSize/m_nItemSize]; }; _MemBlock* m_pMemBlockHeader; _FreeNode* m_pFreeNodeHeader; };
Boost库同样对该技术提供了较好的支持:
pool(#include <boost/pool/pool.hpp>) boost::pool用于快速分配同样大小的小块内存。如果无法分配,返回0,如下所示。 boost::pool<> p(sizeof(double)); //指定每次分配块的大小 if(p!=NULL) { double* const d = (double*)p.malloc(); //为d分配内存 pA.free(d); //将内存还给pool }
pool的析构函数会释放pool占用的内存。
相关文章推荐
- 编写高质量代码之改善C++程序语法篇2<从C到C++需要做的改变>
- <编写高质量改善java程序的151个建议>----笔记
- 编写高质量代码改善C#程序的157个建议——建议29:区别LINQ查询中的IEnumerable<T>和IQueryable<T>
- 改善C++程序的建议:语法篇1<从C继承而来的特性>
- 编写高质量代码改善C#程序的157个建议[勿选List<T>做基类、迭代器是只读的、慎用集合可写属性]
- 编写高质量代码改善C#程序的157个建议——建议23:避免将List<T>作为自定义集合类的基类
- 编写高质量代码改善C#程序的157个建议——建议122:以<Company>.<Component>为命名空间命名
- 编写高质量代码改善C#程序的157个建议[IEnumerable<T>和IQueryable<T>、LINQ避免迭代、LINQ替代迭代]
- 编写高质量代码改善C#程序的157个建议[IEnumerable<T>和IQueryable<T>、LINQ避免迭代、LINQ替代迭代]
- 编写高质量代码改善C#程序的157个建议[勿选List<T>做基类、迭代器是只读的、慎用集合可写属性]
- 编写高质量代码:改善C++程序的150个建议(十八)
- 编写高质量代码:改善C++程序的150个建议(十四)
- 编写高质量代码:改善C++程序的150个建议(九)
- 编写高质量代码:改善C++程序的150个建议(三)
- 编写高质量代码:改善C++程序的150个建议(二)
- 编写高质量代码:改善C++程序的150个建议(五)
- 编写高质量代码:改善C++程序的150个建议(六)
- 编写高质量代码:改善C++程序的150个建议(四)
- 编写高质量代码:改善C++程序的150个建议(十一)