一、 关于new的故事
首先,看看为什么说在C++中尽量用new/delete而不是malloc/free?
因为new/delete会隐式的调用对象的构造函数和析构函数,<Effective C++>第三条用两种方法给一个包含10个string对象的数组分配空间,一个用malloc,另一个用new:
static_cast<string*>(malloc(10 * sizeof(string)));
string *stringarray2 = new string[10];
其结果是,stringarray1确实指向的是可以容纳10个string对象的足够空间,但内存里并没有创建这些对象,而且,没有办法来初始化数组里的对象。换句话说,stringarray1其实一点用也没有。相反,stringarray2指向的是一个包含10个完全构造好的string对象的数组,每个对象可以在任何读取string的操作里安全使用。
假设你想了个怪招对stringarray1数组里的对象进行了初始化,那么在你后面的程序里你一定会这么做:
delete [] stringarray2;
调用free将会释放stringarray1指向的内存,但内存里的string对象不会调用析构函数,如果string对象象一般情况那样,自己已经分配了内存,那这些内存将会全部丢失。相反,当对stringarray2调用delete时,数组里的每个对象都会在内存释放前调用析构函数。做如下测试
{
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,所以经常看到有这样的代码:
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下进行测试
{
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来设置失败的处理函数,比如在处理函数里先释放空闲内存然后再次请求内存分配,其形式为
new_handler set_new_handler(new_handler p) throw();
set_new_handler的形式大致如下, 具体见<Effective C++>第七条
{
new_handler old_handler = current_handler;
current_handler = p;
return old_handler;
}
二、placement new
标准C++支持在一分配的内存上构造对象,这一点在构建内存池时很有效
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
静态快上内存分配(除了不在堆区实际相当于内存池)