Simba

博客园 首页 新随笔 联系 订阅 管理

一、 关于new的故事

 

首先,看看为什么说在C++中尽量用new/delete而不是malloc/free?

因为new/delete会隐式的调用对象的构造函数和析构函数,<Effective C++>第三条用两种方法给一个包含10个string对象的数组分配空间,一个用malloc,另一个用new:

 

string *stringarray1 =
static_cast
<string*>(malloc(10 * sizeof(string)));

string *stringarray2 = new string[10];

 

其结果是,stringarray1确实指向的是可以容纳10个string对象的足够空间,但内存里并没有创建这些对象,而且,没有办法来初始化数组里的对象。换句话说,stringarray1其实一点用也没有。相反,stringarray2指向的是一个包含10个完全构造好的string对象的数组,每个对象可以在任何读取string的操作里安全使用。

假设你想了个怪招对stringarray1数组里的对象进行了初始化,那么在你后面的程序里你一定会这么做:

 

free(stringarray1);
delete [] stringarray2;

 

调用free将会释放stringarray1指向的内存,但内存里的string对象不会调用析构函数,如果string对象象一般情况那样,自己已经分配了内存,那这些内存将会全部丢失。相反,当对stringarray2调用delete时,数组里的每个对象都会在内存释放前调用析构函数。做如下测试

 

class Test
{
public:
    Test(
int size = 100)
    {
        _str 
= new char[size];
        cout 
<< "hold memory in constructor" << endl;
    }
    
~Test(void)
    {
        delete[] _str;
        cout 
<< "release memory in destructor" << endl;
    }
private:
    
char* _str;
};

int main(void)
{
    Test
* pTest = new Test[3];
    delete[] pTest;

    cout 
<< "------------------------------------" << endl;

    Test
* pTest2 = static_cast<Test *>(malloc(3 * sizeof(Test)));
    free(pTest2);

    
return 0;
}

 

 

上面的测试显示了new/delete和malloc/free的区别,如果用new分配了对象然后用free释放有什么后果不难想象。

 

 内存终究是有限的,那么分配内存就可能会失败,在通常自己写的一些小程序里鲜有分配内存失败的情况,但是在分配大块内存、尤其是内存容量较小比如嵌入式系统的时候还是很有可能发生,C语言里分配内存失败的时候就返回一个空值0,所以经常看到有这样的代码:

 

type* p = (type*)malloc(size);
if (p == 0)...

 

C语言中这种处理方式是可行的,但是C++中则未必,早期C++一直要求在内存分配失败时operator new要返回0,现在则是要求operator new抛出std::bad_alloc异常。很多C++程序是在编译器开始支持新规范前写的。c++标准委员会不想放弃那些已有的遵循返回0规范的代码,所以他们提供了另外形式的operator new以及operator new[]以继续提供返回0功能。这些形式被称为“无抛出”,他们在使用new的入口点采用了nothrow对象,但是在new内存的时候如果不声明nothrow则内存分配失败时会抛出bad_alloc的异常,这时用如上C语言的方式检测成功与否就未必可行,下面的例子在VS2005下进行测试

 

void test(void)
{
    unsigned 
long size = 1500 * 1024 * 1024;

    
char* mem1 = new (nothrow)char[10 * size];    // 若分配失败返回0 
    
if (!mem1)                                    // 这个检查可能会成功
    {
        cout 
<< "allocate 1 failed" << endl;
    }

    
char* mem2 = new char[10 * size];             // 分配失败抛出std::bad_alloc 
    
if (!mem2)                                    // 这个检查一定失败
    {
        cout 
<< "allocate 2 failed" << endl;
    }
}

 

测试结果是"allocate 1 failed",由于没有处理程序在分配mem2失败时的异常,因此执行到分配mem2的时候崩溃,解决方法是用try{}catch(std::bad_alloc&)捕获内存分配失败的异常。

 

另外,C++里用set_new_handler来设置失败的处理函数,比如在处理函数里先释放空闲内存然后再次请求内存分配,其形式为

typdef void (*new_handler)();
new_handler set_new_handler(new_handler p) 
throw();

 

set_new_handler的形式大致如下, 具体见<Effective C++>第七条

new_handler set_new_handler(new_handler p)
{
    new_handler old_handler 
= current_handler;
    current_handler 
= p;
    
return old_handler;
}

 

二、placement new

 

标准C++支持在一分配的内存上构造对象,这一点在构建内存池时很有效

 

Operator new allocates memory from the heap, on which an object is constructed. Standard C++ also supports placement new operator, which constructs an object on a pre-allocated buffer. This is useful when building a memory pool, a garbage collector or simply when performance and exception safety are paramount (there's no danger of allocation failure since the memory has already been allocated, and constructing an object on a pre-allocated buffer takes less time):
 
void placement() {

char *buf  = new char[1000];   //pre-allocated buffer
	string *p = new (buf) string("hi");  //placement new
	string *q = new string("hi");  //ordinary heap allocation
	cout<c_str()<c_str();<

 

可参考的博文:

  

参加内存池管理

http://www.ecjtu.org/forum/read.php?tid-25583.html

 

代码实现??

placement new

http://www.cppblog.com/jacky2019/archive/2007/04/06/21375.html

 http://www.ecjtu.org/forum/read.php?tid-25583.html

静态快上内存分配(除了不在堆区实际相当于内存池)

http://www.cppblog.com/expter/archive/2009/08/16/93511.aspx

posted on 2010-05-25 15:40  Simba Yang  阅读(501)  评论(0编辑  收藏  举报