您的位置:首页 > 编程语言 > C语言/C++

编写高质量代码之改善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

这是我们使用最多的

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占用的内存。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐