C++编程中new运算符的使用学习教程
new运算符用作从自由存储为type-name的对象或对象数组分配内存,并将已适当分类的非零指针返回到对象。
[::]new[placement]new-type-name[new-initializer] [::]new[placement](type-name)[new-initializer]
备注
如果不成功,则new将返回零或引发异常;有关详细信息,请参阅new和delete运算符。通过编写自定义异常处理例程并调用_set_new_handler运行库函数(以您的函数名称作为其参数),可以更改此默认行为。
有关如何在托管堆上创建对象的信息,请参阅gcnew。
使用new为C++类对象分配内存时,将在分配内存后调用对象的构造函数。
使用delete运算符可解除分配使用new运算符分配的内存。
以下示例先分配然后释放一个二维字符数组,数组的大小为dimx10。在分配多维数组时,除第一个维度之外的所有维度必须是计算结果为正值的常量表达式;最左侧的数组维度可以是计算结果为正值的任何表达式。在使用new运算符分配数组时,第一个维度可为零-new运算符将返回一个唯一指针。
char(*pchar)[10]=newchar[dim][10]; delete[]pchar;
type-name不能包含const、volatile、类声明或枚举声明。因此,以下表达式是非法的:
volatilechar*vch=newvolatilechar[20];
new运算符不会分配引用类型,因为这些类型不是对象。
new运算符无法用于分配函数,但可用于分配指向函数的指针。下面的示例为返回整数的函数分配然后释放一个包含7个指针的数组。
int(**p)()=new(int(*[7])()); delete*p;
如果使用不带任何额外参数的new运算符,并用/GX、/EHa或/EHs选项进行编译,则编译器将在构造函数引发异常时生成代码来调用运算符delete。
以下列表描述了new的语法元素:
placement
如果重载new,则提供了一种传递附加参数的方式。
type-name
指定要分配的类型;它可以是内置类型,也可以是用户定义的类型。如果类型规范非常复杂,则可用括号将其括起来以强制实施绑定顺序。
initializer
为初始化对象提供值。不能为数组指定初始值设定项。仅当类具有默认构造函数时,new运算符才会创建对象的数组。
示例
下面的代码示例分配类CName的一个字符数组和一个对象,然后释放它们。
//expre_new_Operator.cpp //compilewith:/EHsc #include<string.h> classCName{ public: enum{ sizeOfBuffer=256 }; charm_szFirst[sizeOfBuffer]; charm_szLast[sizeOfBuffer]; public: voidSetName(char*pszFirst,char*pszLast){ strcpy_s(m_szFirst,sizeOfBuffer,pszFirst); strcpy_s(m_szLast,sizeOfBuffer,pszLast); } }; intmain(){ //Allocatememoryforthearray char*pCharArray=newchar[CName::sizeOfBuffer]; strcpy_s(pCharArray,CName::sizeOfBuffer,"Arrayofcharacters"); //Deallocatememoryforthearray delete[]pCharArray; pCharArray=NULL; //Allocatememoryfortheobject CName*pName=newCName; pName->SetName("Firstname","Lastname"); //Deallocatememoryfortheobject deletepName; pName=NULL; }
如果使用new运算符的放置新形式(带有参数和分配大小的形式),如果构造函数引发异常,则编译器不支持delete运算符的放置形式。例如:
//expre_new_Operator2.cpp //C2660expected classA{ public: A(int){throw"Fail!";} }; voidF(void){ try{ //heapmemorypointedtobypa1willbedeallocated //bycalling::operatordelete(void*). A*pa1=newA(10); }catch(...){ } try{ //Thiswillcall::operatornew(size_t,char*,int). //WhenA::A(int)doesathrow,weshouldcall //::operatordelete(void*,char*,int)todeallocate //thememorypointedtobypa2.Since //::operatordelete(void*,char*,int)hasnotbeenimplemented, //memorywillbeleakedwhenthedeallocationcannotoccur. A*pa2=new(__FILE__,__LINE__)A(20); }catch(...){ } } intmain(){ Aa; }
初始化使用new运算符分配的对象
可选的initializer字段包含在new运算符的语法中。这样就可以使用用户定义的构造函数来初始化新对象。有关如何执行初始化的详细信息,请参阅初始值设定项。以下示例演示如何将初始化表达式与new运算符一起使用:
//expre_Initializing_Objects_Allocated_with_new.cpp classAcct { public: //Definedefaultconstructorandaconstructorthataccepts //aninitialbalance. Acct(){balance=0.0;} Acct(doubleinit_balance){balance=init_balance;} private: doublebalance; }; intmain() { Acct*CheckingAcct=newAcct; Acct*SavingsAcct=newAcct(34.98); double*HowMuch=newdouble(43.0); //... }
在此示例中,使用CheckingAcctnew运算符分配了对象,但未指定默认初始化。因此,调用了类的默认构造函数Acct()。然后,以相同的方式分配了对象SavingsAcct,只不过将它显式初始化为34.98。由于34.98是类型double,因此调用了采用该类型的参数的构造函数来处理初始化。最后,将非类类型HowMuch初始化为43.0。
如果对象是类类型,并且该类具有构造函数(如前面的示例所示),则仅当满足以下条件之一时,new运算符才能初始化该对象:
初始值设定项中提供的参数与构造函数的参数一致。
该类有一个默认构造函数(可在没有参数的情况下调用的构造函数)。
访问控制和二义性控制根据operatornew多义性和使用特殊成员函数的初始化中所述的规则对和构造函数执行。
在使用new运算符分配数组时,无法对每个元素执行显式初始化;只调用了默认构造函数(如果有)。有关详细信息,请参阅默认参数。
如果内存分配失败(operatornew的返回值为0),则不执行初始化。这可防止尝试初始化不存在的数据。
与函数调用一样,未定义初始化表达式的计算顺序。此外,您不应指望这些表达式能在执行内存分配前完全计算。如果内存分配失败,并且new运算符返回零,则可能不会完全计算初始值设定项中的某些表达式。
使用new运算符分配的对象的生存期
在退出分配有new运算符的对象的定义范围时,将不会销毁这些对象。由于new运算符将返回指向其所分配的对象的指针,因此程序必须使用合适的范围定义指针才能访问这些对象。例如:
//expre_Lifetime_of_Objects_Allocated_with_new.cpp //C2541expected intmain() { //Usenewoperatortoallocateanarrayof20characters. char*AnArray=newchar[20]; for(inti=0;i<20;++i) { //Onthefirstiterationoftheloop,allocate //anotherarrayof20characters. if(i==0) { char*AnotherArray=newchar[20]; } } delete[]AnotherArray;//Error:pointeroutofscope. delete[]AnArray;//OK:pointerstillinscope. }
在上面的示例中,指针AnotherArray一旦超出范围,将无法再删除对象。
new的工作方式
allocation-expression(包含new运算符的表达式)执行三类操作:
定位并保留要分配的对象的存储。此阶段完成后,将分配正确的存储量,但它还不是对象。
初始化对象。初始化完成后,将为成为对象的已分配存储显示足够的信息。
返回指向派生自new-type-name或type-name的指针类型的对象的指针。程序使用此指针来访问最近分配的对象。
new运算符调用函数operatornew。对于任何类型的数组以及不属于class、struct或union类型的对象,调用全局函数::operatornew来分配存储。类类型对象可基于每个类定义其自己的operatornew静态成员函数。
当编译器遇到用于分配type类型的对象的new运算符时,它将发出对type::operatornew(sizeof(type))的调用;或者,如果不存在用户定义的operatornew,则调用::operatornew(sizeof(type))。因此,new运算符可以为对象分配正确的内存量。