new和delete的工作原理
new和delete是C++新引入的单目运算符,它们可以从堆上分配和删除存储块(堆在C++中也叫自由存储).用new运算符(不是函数)时要指明数据类型,以后new就分配一个足以放下指明类型对象的存储,并返回该存储块的首地址作为指向指定类型的指针.
1. new/new[]:完成两件事,先底层调用malloc分了配内存,然后创建一个对象(调用构造函数)。
2. delete/delete[]:也完成两件事,先调用析构函数(清理资源),然后底层调用free释放空间。
3. new在申请内存时会自动计算所需字节数,而malloc则需我们自己输入申请内存空间的字节数。
new/delete,new[]/delete[],malloc/free必须配对使用。
重载new和delete
使用了new和delete的内存分配系统是为通用目的而设计的。但在特殊情况下,它不能满足需要(比如假设默认的分配策略适合分配小的内存块,而我们经常需要分配大块内存)。C/C++允许重载new和delete来实现我们自己的内存分配策略。
当重载new和delete时,我们只是改变了原有的内存分配策略,记住这点很关键。编译器将用重载后的new代替默认版本取分配内存,然后为那个内存块调用构造函数。所以,当编译器看到new时,编译器分配内存并调用构造函数,但是当重载new时,可以改变的只是内存分配部分(delete也一样)。
重载new和delete与重载其他运算符一样。但可以选择重载全局内存分配函数或者是特定类的内存分配函数。
重载全局的new和delete
当全局版本的new和delete不能满足需要时,对其重载是很极端的方法。如果重载了全局版本,那么默认版本将完全无法访问(在这个重新定义里也不能调用它们)。
重载的new必须有一个size_t类型的参数,这个参数由编译器产生并传递给我们,它是要分配内存的对象的长度(使用new时,后面接一个对象,编译器会知道这个对象的大小)。必须返回一个指向等于(或者大于)这个长度的对象的指针,如果没有找到可用内存,则返回0。然而如果找不到可用内存,不能仅仅返回0,也许还应该做一些诸如调用new-handler或者产生一个异常信息之类的事。
operator new()返回值是一个void*,而不是指向某个具体类型的指针。所做的事情只是分配内存,而不是完成一个对象的建立——直到调用了构造函数才算完成了对象的构建,它是编译器确保的动作,不在我们可控范围内。
oeprator delete()的参数是一个指向由operator new()分配的内存的void*。参数是一个void*是因为它是调用析构函数之后得到的指针。析构函数从存储单元中移除对象。operator delete()返回值是void。
对于一个类重载new和delete
为一个类重载new和delete时,尽管不必显式的使用static,但实际上仍然是创建static成员函数。它的语法也和重载其他运算符一样。当编译器看到使用new创建自己定义的类的对象时,它选择成员版本的operator new()而不是全局版本的new()。但是全局版本的new和delete任然为其他类型所使用。
为一个类数组重载new和delete
如果为一个类重载了operator new和operator delete,那么无论何时创建一个这个类的对象,都会调用重载的new和delete。但如果要创建这个类的一个数组的话,编译器会使用全局的new和delete。所以我们还要重载这个类的new的数组版本。即operator new[]和operator delete[]。
使用数组版本的new时,需要的长度比期望的多了4个字节。这额外的4字节是系统用来存放数组信息的,特别是数组对象的数量。
定位new和delete
重载operator new还有两个不常见的用途:
1、在内存的指定位置放置一个对象。
2、让使用new的程序员可以选择不同的内存分配方案
这两个特性可以用相同的机制实现: 重载的operator new()可以带一个或多个参数。第一个参数总是对象的长度,它在编译器内部计算出来并传递给new,但其他参数可以由我们定义:一个放置对象的地址、一个是对内存分配函数或对象的引用,或其他设置。
在调用过程中传递额外参数给operator new的方法看起来有点古怪:在关键字new后是参数表(没有size_t参数,它由编译器处理),参数后面是正在创建的对象的类名字。例如:
X* xp = new(a) X;
将a作为第二个参数传递个operator new。注意,这是在operator new已经声明的情况下才有效。
网友评论