【推荐】2019 Java 开发者跳槽指南.pdf(吐血整理) >>>
new与delete表达式作了什么
对于new来说
计算所需内存的字节数,然后以此为参数调用标准库的operator new(size_t)函数
在operator new()返回的内存上调用类的适当的构造函数初始化一个对象
将operator new() 返回的指针作为表达式的运算结果
也即 new 把内存分配与对象构造合在一起了
对于delete来说
调用指针所指对象的析够函数释放对象本身
调用标准库的operator delete()函数将指针所指内存返还给系统
也即 delete 把释放对象本身与释放对象所占的内存合在一起
new[] 与 delete[] 行为与上类似
allocator类
很多时候并不需要立即在分配的内存上初始化一个对象,与此同时也希望在释放内存之前不调用对象的析钩函数;allocator类提供了这种机制,其成员:
allocator<T> a; /* a可以用来分配原始内存以及在原始内存上构造T类型对象 */
a.allocate(n); /* 分配原始的内存,可以保存n个Type类型的对象 */
a.deallocate(p,n);/* 释放p所指的原始内存,p必须合法;并且不会调用p所指对象的析钩函数,n表示p所指的内存可以保存n个T类型对象 */
a.construct(p,t);/* 在p所指的内存上调用T类型的复制构造函数(以t为参数)初始化一个对象 */
a.destory(p);/* 调用p所指对象的析钩函数 */
若干个用于在原始内存上构造对象的算法:
uninitialized_copy(begin,end,begin2);
/* 从迭代器[begin,end)指出的输入范围将元素复制到从迭代起begin2
* 开始的未构造的原始内存中,该函数是在目的地构造对象,而不是赋值 */
uninitialized_fill(begin,end,t)
/* 将由迭代器[begin,end)指出的范围中的对象初始化为t的副本
* [begin,end)范围是未构造的原始内存,使用复制构造函数构造对象 */
uninitialized_fill_n(begin,end,t,n)
/* 同上,不过至多构造n个对象 */
operator new/operator delete
标准库提供了下列函数用于分配/释放原始的未构造的内存:
void* operator new(size_t) /* new表达式调用 */
void* operator new[](size_t) /* new[]表达式调用 */
void operator delete(void*)
void operator delete[](void*)
定位new表达式
用来在已经分配好的,原始的内存上调用构造函数初始化对象,与其他new表达式不同,定位new表达式不会分配新的内存,其语法:
/* 在已经分配的原始内存上调用对象的默认构造函数构造对象 */
new(原始内存指针) Type
/* 根据参数类型与参数个数调用合适的构造函数构造对象 */
new(原始内存指针) Type(初始化参数)
#include <stdio.h>
#include <new>
#include <malloc.h>
class Test{
public:
Test(){ printf("1\n"); }
Test(int __a,int __b){ printf("2\n"); }
~Test(){ ; }
};
int main(int argc,char *argv[]){
Test *ptr=(Test*)malloc(sizeof(Test));
new(ptr)Test;
ptr->~Test();
new(ptr)Test(3,3);
ptr->~Test();
free(ptr);
return 0;
}
自定义new与delete
不能重定义new与delete表达式的行为,但是可以自定义new与delete表达式的行为
当编译器看到类类型的new或delete表达式时,她查看该类是否有operator new 或operator delete成员
类定义(或继承)了自己的成员new和delete函数,则使用这些函数为对象分配/释放原始内存
否则,调用这些函数的标准库版本
#include <stdio.h>
#include <new>
#include <malloc.h>
class Test{
public:
void* operator new(size_t __size){
printf("1\n");
return ::operator new(__size);
}
};
int main(int argc,char *argv[]){
Test *test=new Test;
delete test;
return 0;
}
自定义operator new与operator delete时只要注意参数类型,返回值类型即可
#include <stdio.h>
#include <new>
#include <malloc.h>
class Test{
public:
void operator delete(void* __p,size_t __size){
printf("Size: %zd\n",__size);
/* __size可有可无,若__size存在,编译器会把'delete p'
* p所指对象所占内存字节传递给__size */
::operator delete(__p);
return ;
}
};
int main(int argc,char *argv[]){
Test *test=new Test;
delete test;
return 0;
}
在类继承层次中自定义new与delete
对于operator new:
#include <stdio.h>
#include <new>
#include <malloc.h>
class A{
public:
void* operator new(size_t __size){
printf("A\n");
return ::operator new(__size);
}
};
class B:public A{
public:
void* operator new(size_t __size){
printf("B\n");
return ::operator new(__size);
}
};
int main(int argc,char *argv[]){
A *a=new A;/* 调用A的operator new */
A *b=new B;/* 调用B的operator new */
/* 分析new表达式的行为->与 B *b=new B 是一样的情况 */
delete a;
delete b;
return 0;
}
对于 operator delete,B继承A,当B中未定义operator delete时:
#include <stdio.h>
#include <new>
#include <malloc.h>
class A{
public:
void operator delete(void *__p,size_t __size){
printf("Size: %zd\n",__size);
::operator delete(__p);
}
virtual ~A(){ ; }
};
class B:public A{
int a;
};
int main(int argc,char *argv[]){
A *a=new A;
A *b=new B;
delete a;/* a->A类型的对象,所以__size=sizeof(A) */
delete b;
/* 由于A中存在虚析够函数,所以编译器会将b所指动态类型的
* 尺寸传递给__size,即__size=sizeof(B) */
return 0;
}
当B中也定义了operator delete时:
#include <stdio.h>
#include <new>
#include <malloc.h>
class A{
public:
void operator delete(void *__p,size_t __size){
printf("A:Size: %zd\n",__size);
::operator delete(__p);
}
virtual ~A(){ ; }
};
class B:public A{
int a;
void operator delete(void *__p,size_t __size){
printf("B:Size: %zd\n",__size);
::operator delete(__p);
}
};
int main(int argc,char *argv[]){
A *a=new A;
A *b=new B;
delete a; /* 调用A::operator delete */
delete b; /* 调用的是B::operator delete */
return 0;
}
在delete b 中调用的 B::operator delete 有一点迷惑不解,由于 operator new()与operator delete()在类中默认是静态成员函数(因为当 A *a=new A时,分配内存会先于对象构造,所以应该是静态函数) 所以她们 不可能同时又是虚函数,所以delete b应该调用的是A::operator delete;但是确确实实调用的是B::operator delete;想来大概是编译器的作用,记住就行
自定义new[]/delete[]
与上非常相似,如不在类继承层次中的自定义:
#include <stdio.h>
#include <new>
#include <malloc.h>
class A{
public:
A(){ printf("A\n"); }
void* operator new[](size_t __size){
printf("A:new Size: %zd\n",__size);
return ::operator new[](__size);
}
void operator delete[](void *__p,size_t __size){
printf("A:delete Size: %zd\n",__size);
::operator delete[](__p);
return ;
}
virtual ~A(){ printf("~A\n"); }
};
int main(int argc,char *argv[]){
A *a=new A[4];
delete[] a;/* 会把a所指内存的字节(即数组大小)传递给__size */
return 0;
}
在类继承层次:
#include <stdio.h>
#include <new>
#include <malloc.h>
class A{
public:
A(){ printf("A\n"); }
void* operator new[](size_t __size){
printf("A:new Size: %zd\n",__size);
return ::operator new[](__size);
}
void operator delete[](void *__p,size_t __size){
printf("A:delete Size: %zd\n",__size);
::operator delete[](__p);
return ;
}
void operator delete(void *__p,size_t __size){
printf("A:delete Size: %zd\n",__size);
::operator delete(__p);
return ;
}
virtual ~A(){ printf("~A\n"); }
};
class B:public A{
int b;
public:
B():b(0){ printf("B\n"); }
void* operator new[](size_t __size){
printf("B:new Size: %zd\n",__size);
return ::operator new[](__size);
}
void operator delete[](void *__p,size_t __size){
printf("B:delete Size: %zd\n",__size);
::operator delete[](__p);
return ;
}
void operator delete(void *__p,size_t __size){
printf("B:delete Size: %zd\n",__size);
::operator delete(__p);
return ;
}
virtual ~B(){ printf("~B\n"); }
};
int main(int argc,char *argv[]){
A *b=new B;
delete b;/* 正确释放 */
B *b2=new B[4];
delete[] b2;/* 可以正确释放 */
A *b1=new B[4];
delete[] b1;/* 此时提示段错误,编译器不能识别了... */
// delete[] (B*)b1 /* 即可 */
return 0;
}
注意 delete[] b1 出错!
来源:oschina
链接:https://my.oschina.net/u/1383479/blog/192452