对于有一定编程基础的人来说,new与delete一定会非常的熟悉,知道在使用的过程中:new 先分配memory(内存)再调用ctor(构造函数);而delete是先调用dtor(析构函数)再释放memory。
int * p=new int[10];
int * pa=new int(10);
new用来动态创建数据或者单个对象;对于上面的代码,第一行是用new 动态创建一个长度为10的数据,指针p指向数组的首地址;而代码第二行是用new创建一个单个的对象,单个的对象为整型数10,指针pa指向这个对象的地址。
对于使用方法,在本次博文中不做详细的讲解,本文主要讲解new和delete的重载。请看下面的例子(重载一个全局的new和delete):
//以下代码将new和delete进行了全局重载
void * myAlloc(size_t size)
{
return malloc(size);
}
void myfree(void * ptr)
{
return free(ptr);
}
//它们不可以被申明在一个namespace内
inline
void * operator new(size_t size)
{
cout << "global new "; return myAlloc(size);
}
inline
void * operator new[](size_t size)
{
cout << "global new[] "; return myAlloc(size);
}
inline
void operator delete(void * ptr)
{
cout << "global delete "; return myfree(ptr);
}
inline void operator delete[](void * ptr)
{
cout << "global delete[] "; return myfree(ptr);
}
如果程序中写成上面的代码形式,则此时new和delete被重载成了全局的形式。那么如果对一个class里面重载 member operator new/delete 应该如何操作呢,请看下面的参考代码(如果需要对一个class中的new和delete进行重载可以参考下面的代码(下方代码的“…”部分需要用户根据自己的需求进行自定义)
//以下代码适合在一个class中对new 和delete的重载过程进行理解(仅供参考)
class Foo{
public:
int _id;
long _data;
string _str;
public:
Foo() :_id(0){ cout << "default ctor this= " << this << "id= " << _id << endl; }
Foo(int i) :_id(i){ cout << "ctor this = " << this << "_id" << _id << endl; }
//virtual
~Foo(){ cout << "dtor this= " << this << "id=" << _id << endl; }
static void * operator new(size_t size);
static void operator delete(void * pdead, size_t size);
static void * operator new[](size_t size);
static void operator delete[](void *pdead, size_t size);
};
void * Foo::operator new(size_t size){
Foo * p = (Foo *)malloc(size);
cout << ......;//此处用户自行定义
return p;
}
void Foo::operator delete(void * pdead, size_t size){
cout << ......;
free(pdead);
}
void * Foo::operator new[](size_t size){
Foo * p = (Foo *)malloc(size);
cout << ......;
return p;
}
void Foo::operator delete[](void * pdead, size_t size){
cout << .....;
free(pdead);
}
使用时用法:
Foo * pf = new Foo;
delete pf;
Foo * pd = new Foo[size];
delete [] pd;
通过上方的例子相信大家对于在C++中如何实现new和delete的全局重载,以及在一个class中对new和delete进行重载有了一定的了解,但是我们要记住的是new与delete是配套出现的,尤其是对于new一个数据时,最后必须要有配套的delete对其进行内存的释放。
但是对new和delete使用中如果遇到下方的这种情况则一定需要注意(如下):
Foo * pf=::new Foo;
delete pf;
如果遇到上面的这种情况,即:用户已经在class中对new和delete进行了重载,但是使用中如果遇到上面这种情况(在new的前面加上“::”符号)时,则表示此时绕过了自己重载的运算符new,而是调用了全局的new运算符。
对于上面的代码里面写的class Foo中的数据成分,可以知道,含有一个int型数据,一个long型数据,一个string型数据,而从内存的角度分析:一个int占4个字节,一个long型数据也是占4个字节,而一个string型的数据它实际是一个指针,而一个指针占4个字节,所以此时我们如果要求Foo占得字节,可以得到:
sizeof(Foo)的值为12
& 如果给Foo 类添加一个虚函数时,更具我之前说的虚指针与虚表的分析可以知道当加上了虚函数后,那么它的对象就会多一个指针,而一个指针占4个字节,则加上虚函数后sizeof(Foo)=16.
那么如果对于Foo这个类型,我需要new一个数据类型为Foo的数组,它的内存又是如何变化的呢,如下:
Foo * Array=new Foo[5];
delete [] Array;
那么此时sizeof(Foo)=64;大家可能会觉得奇怪,一个单一的Foo对象sizeof的值是12,那么上面的这个数组对应的sizeof不是应该是12*5=60么,多出的4个字节是哪里来的。其实当new一个数组时,Foo类型这个数组实际占的字节大小等于 Array的整包大小12乘以5等于60,然后在加上一个计数器来记录有几个Foo类型的数据,而这个计数器的大小占4个字节,所以最后他的大小60+4=64个字节。 那么如果与virtual结合呢,他的大小将变成(12+4)*5+4=84个字节。
遇到重载new()和delete()时:
在这之前我们说的new和delete的重载,是operator new;operator delete和operator new[];oprator delete[],除了这两种重载方法外,我们还可以重载class member operator new(),写出多个版本,前提是每一个版本的声明都必须有独特的参数列,其中**第一参数必须是size_t 型,**其余参数以new所指定的placement arguments 为初值,出现new(…),小括号内便是所谓的placement argument 。如:
Foo * pf=new(300,'c')Foo;
我们也可以重载class member operator delete(),写出多个版本,但他们绝不会被delete调用,只有当new所调用挂的ctor(构造函数)跑出exception时才会调用这些重载版的operator delete(),它只可以这样来调用,主要用来归还未能完全创建成功的object所占用的memory。
下方为new()的参考:
void * oprerator new(size_t size ,void * start){
return start;
}
void * operator new(size_t size ,long extra){
return malloc(size+extra);
}
void * operator new(size_t size ,long extra,char inti){
return malloc(size+extra);
}
对于文中错误的地方或者表述不正确的地方,还请大家能够在评论区给与指正,谢谢!
C++更多文章请点击这里