详解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; }