动态创建对象(2)
5)重载new 与delete,首先为什么需要重载new与delete呢?
① 也许要创建和销毁一个类的非常多的对象,以至于这个影响到了速度。
② 分配不同大小的内存可能会产生碎片,内存很快就被分配完
所以重载的时候我们只是改变了原来的分配方法,当重载时可以替换原来内存消耗完之后的行为。所以在operator new 中要决定是返回0,还是调用new-hander的循环。或者产生异常的消息。我们可以选择重载全局内存分配函数或者是针对于某个类。
(1)这是重载全局的new以及delete之后的程序以及运行结果:
#include <cstdio>
#include <cstdlib>
#include <iostream>
using namespace std;
void * operator new(size_t sz)//1.size_t 的类型是?标准C库中定义的,应为unsigned int,它是一种“整型”类型,
{ //里面保存的是一个整数,就像int, long那样。
//printf("operator new :%d Bytes\n",sz);//2.这里使用的是printf,而不是cout,因为它是一个iostream对象,会调用new去分配内存,于是产生死锁
cout<<"operator new "<<sz<<endl;
void *m=malloc(sz);//3.申请空间返回的是void* 类型,所以它所做的只是分配内存,而非完成一个对象的创建。对象的创建要等到编译器调用构造函数才完成
if (!m)
{
puts("out of memeory");
}
return m;
}
void operator delete(void *m)//参数是void* 这是调用析构函数后得到的指针,也就是说析构函数会从构造函数中获得内存的指针
{ //返回类型为void
puts("operator delete");
free(m);
}
class S
{
int i[100];
public:
S()
{
puts("S::S()");//
}
//void operator delete[](void *m)//这种方式可以删除对象数组
//{
// puts("operator array delete");
// ::delete []m;
//}
~S()
{
puts("S::~S()");
}
};
int GlobalOperatorNewTest()
{
puts("creating & destorying an int");
int *p=new int(47);
delete p;
puts("creating &destorying an S");
S *s=new S;
delete s;
puts("creating &destorying S[3]");
S *sa=new S[3];//4.这里会多出空间,用于存放数组的信息,也就是说对象的数量的信息。
delete []sa;
return 0;
}
结果:
从结果中我们可以看到首先会调用new然后再调用的是构造函数。调用了析构函数之后才会调用deleted函数。但是我们看到在释放对象数组的时候并没有调用我们自己定义的deleted函数。而是调用的系统的delete[],
那么如果在类中对数组进行delete的重载如下:
void operator delete[](void *m)//这种方式可以删除对象数组
{
puts("operator array delete");
::delete []m;
}
这是进行重载数组的delete函数之后的执行结果
(1)也可以在类中进行new的重载,只是针对这个类。
代码:
//operator new local
#include <cstddef>
#include <fstream>
#include <iostream>
#include <new>
using namespace std;
ofstream out("Framis01.txt");
class Framis
{
enum{sz=10};
char c[sz];
static unsigned char pool[];
static bool alloc_map[];
public:
enum{prize=100};
Framis()
{
out<<"Framis()\n";
}
~Framis()
{
out<<"~Framis()...";
}
#pragma warning( disable : 4290 )
void *operator new (size_t) throw(bad_alloc);
void operator delete(void *);
};
unsigned char Framis::pool[prize*sizeof(Framis)];
bool Framis::alloc_map[prize]={false};
void * Framis::operator new(size_t )throw(bad_alloc)//参数后面带throw告诉编译器可以产生一个bad_alloc的异常消息。且若没有内存可以使用,也会由
{ //bad_alloc产生一个异常消息。
//使用类的这种创建方法,可以减少内存碎片。那么释放掉的内存可以重新使用
for (int i=0;i<prize;i++)
{
if(!alloc_map[i])//判断这块内存有没有被使用,
{
out<<"using block"<<i<<"...";
alloc_map[i]=true;//没有使用的话,将其置为true
return pool+(i*sizeof(Framis));//返回地址
}
}
cout<<"out of memeory"<<endl;
throw bad_alloc();
}
void Framis::operator delete(void *m)
{
if(!m)//确保是地址在堆内是在正确的地址范围之内,因为很可能我们删除的是数组,然而忘记加上[]。
return;
unsigned long block=(unsigned long)m-(unsigned long)pool;
block/=sizeof(Framis);
out<<"freeing block "<<block<<endl;//释放对应的地址空间.
alloc_map[block]=false;
}
int main()
{
Framis *f[Framis::prize];
try{
for (int i=0;i<Framis::prize;i++)
{
f[i]=new Framis ;
}
new Framis;
}catch(bad_alloc)//在这里接收内存申请失败的信息。
{
cerr<<"Out of memory!"<<endl;
}
delete f[10];//这是调用全局版本的delete.
f[10]=0;//删除对应的空间内容,并且使这个指针的值为0.
Framis *x=new Framis;//当重新创建一个对象,对象的内容将会被放在f[10]所指的内存中,这样就充分的利用资源
delete x;
for (int j=0;j<Framis::prize;j++)
{
delete f[j];
}
}
运行结果:
可以看到释放的内存重新被利用起来。下面是txt中的一部分内容
using block99...Framis()
~Framis()...freeing block 10
using block10...Framis()
~Framis()...freeing block 10
(1)定位new.也就是确定在哪个位置上进行分配内存,也就是可以向New函数中传递参数,但是对于delete是不能传递的,它是与new对应的。
小结:1、C中的malloc虽然可以使用但是不安全,
2、如果对内存要求很高的时候我们不妨使用自己定义的new来申请内存空间