//1.控制内存分配: //A:某些应用程序对内存分配有特殊的需求,因此我们无法将标准内存管理机制直接应用于这些程序。它们常常需要自定义内存分配的细节,比如使用关键字new将对象放置在特定的内存空间中。 //B:如下代码: string *sp = new string("s"); string *arr = new string[10]; // new系列运算符实际上执行了三步操作: // 第一步:new表达式调用一个名为operator new(或者operator new[])的标准库函数。该函数分配一块足够大的、原始的、未经构造的内存空间。 // 第二步:编译器运行相应的构造函数以构造这些对象,并为其传入初始值。 // 第三步:对象被分配了空间并完成构造,返回一个指向该对象的指针。 delete sp; delete []arr; // delete系列运算符实际上执行了两步操作: // 第一步:对sp所指对象或者arr所指的数组中的元素执行对应的析构函数。 // 第二步:编译器调用名为operator delete(或者operator delete[])的标准库函数释放空间。 // 如果程序希望控制内存分配的过程,则需要自己定义operator new函数和operator delete函数。这两个函数的查找过程为:首先在对应类的类空间中查找(若操作的对象是类类型的话),其次在全局空间中查找,最后会调用标准库的版本。 //C:标准库定义的operator new系列函数和operator delete系列函数,其中带nothrow_t的是不抛出异常的版本: _Ret_bytecap_(_Size) void *__CRTDECL operator new(size_t _Size) _THROW1(_STD bad_alloc); _Ret_bytecap_(_Size) void *__CRTDECL operator new[](size_t _Size); _Ret_opt_bytecap_(_Size) void *__CRTDECL operator new(size_t _Size, const _STD nothrow_t&); _Ret_opt_bytecap_(_Size) void *__CRTDECL operator new[](size_t _Size, const _STD nothrow_t&); void __CRTDECL operator delete(void *) _THROW0(); void __CRTDECL operator delete[](void *) _THROW0(); void __CRTDECL operator delete(void *, const _STD nothrow_t&); void __CRTDECL operator delete[](void *, const _STD nothrow_t&); struct nothrow_t { // placement new tag type to suppress exceptions }; extern const nothrow_t nothrow; // constant for placement new tag // 应用程序可以自定义上述的任何版本,前提是自定义的版本必须位于全局作用域或者是类作用域。当将上述函数定义为类的成员函数的时候,其是隐式静态的,因为operator new系列函数用于对象构造之前,而operator delete系列函数用于对象销毁之后,所以此类函数不能操作类的数据成员。 // 对于operator new系列的函数,它们的返回值必须是void*,第一个形参类型必须是size_t且不能有默认实参,此参数用于保存构造内存所需的字节数。 // 如果想要自定义operator new系列函数,则可以为其提供额外的参数。此时,用到这些自定义函数的new表达式必须使用new的定位形式将实参传递给新增的形参。 void *__CRTDECL operator new(size_t _Size, int nValue) { printf("%d %d\n", _Size, nValue); return malloc(4); } int *p = new(20) int(10); //p = 0x000e36b0 *p = 10; 输出4 20 //D:尽管在一般情况下我们可以自定义具有任何形参的operator new,但是下面这个函数却无论如何不能被用户重载: inline void *__CRTDECL operator new(size_t, void *_Where) _THROW0() { // construct array with placement at _Where return (_Where); } inline void *__CRTDECL operator new[](size_t, void *_Where) _THROW0() { // construct array with placement at _Where return (_Where); } inline void __CRTDECL operator delete(void *, void *) _THROW0() { // delete if placement new fails } inline void __CRTDECL operator delete[](void *, void *) _THROW0() { // delete if placement array new fails } // 上述的operator new 和 operator delete系列函数是供标准库调用的,所以不应该去重新定义 //E:对于operator delete系列的函数,它们的返回值类型必须是void,第一个参数必须是void*类型。执行一条delete表达式将调用相应的operator函数,并用待释放的指针来初始化void*形参。 // 当我们将operator delete系列的函数定义为类的成员时,该函数可以包含另一个类型为size_t的形参。此时,该形参的初始值是第一个形参所指对象的字节数。size_t形参可用于删除继承体系中的对象。 // 若基类有一个虚析构函数,则传递给operator delete系列函数的字节数将因待删除指针所指对象的动态类型不同而有所区别,实际运行的operator delete函数的版本也会发生变化。 class CFather { public: CFather(){} virtual ~CFather() {printf("0\n");} public: void operator delete(void *p, size_t size) {free(p); printf("1 %d\n", size);} public: int value; }; class CChild : public CFather { public: CChild(){} ~CChild(){printf("2\n");} public: int value0; int value1; public: void operator delete(void *p, size_t size) {printf("3 %d\n", size); free(p);} }; CChild *pChild = new CChild; CFather *pTem = pChild; delete pTem; //输出2 0 3 16 //F:我们提供operator new 和operator delete系列函数的目的在于改变内存分配的方式,但是不管怎样,我们都无法改变new运算符和delete运算符的基本含义。 //G:在自己定义的operator new系列函数和operator delete系列函数中使用malloc和free(均定义在头文件cstdlib中)来完成内存的分配和释放。 //H:定位new形式(包含头文件new):将调用前面所述的不能被重新定义的new的版本 int *pInt = new int(10); //调用标准库版本 pInt = 0x00251510 *pInt= 10 int *pInt1 = new(pInt) int(50); //调用标准库版本 pInt1 = 0x00251510 *pInt1 = 50 *pInt = 50 // 当仅通过一个地址调用时,定位new使用operator new(size_t, void*);该函数不分配任何内存,仅仅是返回指针实参。具体定义和其对应的delete具体见条款D。 // 事实上,定位new允许我们在一个特定的、预先分配的内存上构造对象。这块内存甚至可以不是动态内存。 class CTest { public: ~CTest(){printf("0\n");} }; CTest *Test0 = new CTest; Test0->~CTest(); //调用析构函数会销毁对象但是并不释放内存 CTest *Test1 = new(Test0) CTest; //重用内存 delete Test1; //释放内存
时间: 2024-10-26 21:36:23