详解C++中new运算符和delete运算符的使用
C++支持使用new和delete运算符动态分配和释放对象。这些运算符为来自称为“自由存储”的池中的对象分配内存。new运算符调用特殊函数operatornew,delete运算符调用特殊函数operatordelete。
在VisualC++.NET2002中,标准C++库中的new功能将支持C++标准中指定的行为,如果内存分配失败,则会引发std::bad_alloc异常。
如果内存分配失败,C运行库的new函数也将引发std::bad_alloc异常。
如果您仍需要C运行库的new的非引发版本,请将您的程序链接到nothrownew.obj。但是,当您链接到nothrownew.obj时,标准C++库中的new将不再起作用。
调用new运算符
在程序中遇到以下语句时,它将转换为对函数operatornew的调用:
char*pch=newchar[BUFFER_SIZE];
如果请求针对零字节存储,operatornew将返回一个指向不同的对象的指针(即对operatornew的重复调用将返回不同的指针)。如果分配请求没有足够的内存,则operatornew将返回NULL或引发异常(有关详细信息,请参阅)。
可以编写尝试释放内存的例程并重试分配;有关详细信息,请参阅_set_new_handler。有关恢复方案的更多详细信息,请参阅以下主题:处理内存不足的情况。
下表中描述了operatornew函数的两个范围。
operatornew函数的范围
| 运算符 | 范围 |
|---|---|
| ::operatornew | 全局 |
| class-name::operatornew | 类 |
operatornew的第一个参数的类型必须为size_t(STDDEF.H中定义的类型),并且返回类型始终为void*。
在使用new运算符分配内置类型的对象、不包含用户定义的operatornew函数的类类型的对象和任何类型的数组时,将调用全局operatornew函数。在使用new运算符分配类类型的对象时(其中定义了operatornew),将调用该类的operatornew。
为类定义的operatornew函数是静态成员函数(因此,它不能是虚函数),该函数隐藏此类类型的对象的全局operatornew函数。考虑new用于分配内存并将内存设为给定值的情况:
//spec1_the_operator_new_function1.cpp
#include<malloc.h>
#include<memory.h>
classBlanks
{
public:
Blanks(){}
void*operatornew(size_tstAllocateBlock,charchInit);
};
void*Blanks::operatornew(size_tstAllocateBlock,charchInit)
{
void*pvTemp=malloc(stAllocateBlock);
if(pvTemp!=0)
memset(pvTemp,chInit,stAllocateBlock);
returnpvTemp;
}
//FordiscreteobjectsoftypeBlanks,theglobaloperatornewfunction
//ishidden.Therefore,thefollowingcodeallocatesanobjectoftype
//Blanksandinitializesitto0xa5
intmain()
{
Blanks*a5=new(0xa5)Blanks;
returna5!=0;
}
用括号包含的提供给new的参数将作为Blanks::operatornew参数传递给chInit。但是,全局operatornew函数将被隐藏,从而导致以下代码生成错误:
Blanks*SomeBlanks=newBlanks;
在VisualC++5.0和早期版本中,使用new运算符分配的非类类型和所有数组(无论其类型是否为class)始终使用全局operatornew函数。
从VisualC++5.0开始,编译器支持类声明中的成员数组new和delete运算符。例如:
//spec1_the_operator_new_function2.cpp
classMyClass
{
public:
void*operatornew[](size_t)
{
return0;
}
voidoperatordelete[](void*)
{
}
};
intmain()
{
MyClass*pMyClass=newMyClass[5];
delete[]pMyClass;
}
处理内存不足
对失败的内存分配进行测试可以通过如下编码实现:
//insufficient_memory_conditions.cpp
//compilewith:/EHsc
#include<iostream>
usingnamespacestd;
#defineBIG_NUMBER100000000
intmain(){
int*pI=newint[BIG_NUMBER];
if(pI==0x0){
cout<<"Insufficientmemory"<<endl;
return-1;
}
}
处理失败的内存分配要求的其他方法:编写自定义恢复例程来处理此类失败,然后通过调用_set_new_handler运行时函数来注册您的函数。
delete运算符
可使用delete运算符释放使用new运算符动态分配的内存。delete运算符调用operatordelete函数,该函数将内存释放回可用池。使用delete运算符也会导致调用类析构函数(如果有)。
存在全局和类范围的operatordelete函数。只能为给定类定义一个operatordelete函数;如果定义了该函数,它会隐藏全局operatordelete函数。始终为所有类型的数组调用全局operatordelete函数。
全局operatordelete函数(如果已声明)采用void*类型的单个参数,该参数包含指向要释放的对象的指针。返回类型是void(operatordelete无法返回值)。类成员operatordelete函数有两种形式:
voidoperatordelete(void*); voidoperatordelete(void*,size_t);
给定类中只存在前面两个变量中的一个。第一个形式按照为全局operatordelete描述的那样运行。第二个形式采用两个参数,第一个是指向要释放的内存块的指针,第二个是要释放的字节的数量。当基类中的operatordelete函数用于删除派生类的对象时,第二个形式特别有用。
operatordelete函数是静态的;因此它不能是虚函数。operatordelete函数服从访问控制,如成员访问控制中所述。
以下示例显示旨在记录内存的分配和释放的用户定义的operatornew和operatordelete函数:
//spec1_the_operator_delete_function1.cpp
//compilewith:/EHsc
//arguments:3
#include<iostream>
usingnamespacestd;
intfLogMemory=0;//Performlogging(0=no;nonzero=yes)?
intcBlocksAllocated=0;//Countofblocksallocated.
//User-definedoperatornew.
void*operatornew(size_tstAllocateBlock){
staticintfInOpNew=0;//Guardflag.
if(fLogMemory&&!fInOpNew){
fInOpNew=1;
clog<<"Memoryblock"<<++cBlocksAllocated
<<"allocatedfor"<<stAllocateBlock
<<"bytes\n";
fInOpNew=0;
}
returnmalloc(stAllocateBlock);
}
//User-definedoperatordelete.
voidoperatordelete(void*pvMem){
staticintfInOpDelete=0;//Guardflag.
if(fLogMemory&&!fInOpDelete){
fInOpDelete=1;
clog<<"Memoryblock"<<cBlocksAllocated--
<<"deallocated\n";
fInOpDelete=0;
}
free(pvMem);
}
intmain(intargc,char*argv[]){
fLogMemory=1;//Turnloggingon
if(argc>1)
for(inti=0;i<atoi(argv[1]);++i){
char*pMem=newchar[10];
delete[]pMem;
}
fLogMemory=0;//Turnloggingoff.
returncBlocksAllocated;
}
前面的代码可用于检测“内存溢出”,即在自由储存中分配但从未释放过的内存。若要执行此检测,则应重新定义全局new和delete运算符以计算内存的分配和释放。
从VisualC++5.0开始,编译器支持类声明中的成员数组new和delete运算符。例如:
//spec1_the_operator_delete_function2.cpp
//compilewith:/c
classX{
public:
void*operatornew[](size_t){
return0;
}
voidoperatordelete[](void*){}
};
voidf(){
X*pX=newX[5];
delete[]pX;
}