C++ new 与 delete

可紊 提交于 2019-12-13 14:05:21

【推荐】2019 Java 开发者跳槽指南.pdf(吐血整理) >>>

new与delete表达式作了什么

对于new来说

  1. 计算所需内存的字节数,然后以此为参数调用标准库的operator new(size_t)函数

  2. 在operator new()返回的内存上调用类的适当的构造函数初始化一个对象

  3. 将operator new() 返回的指针作为表达式的运算结果

也即 new 把内存分配与对象构造合在一起了

对于delete来说

  1. 调用指针所指对象的析够函数释放对象本身

  2. 调用标准库的operator delete()函数将指针所指内存返还给系统

也即 delete 把释放对象本身与释放对象所占的内存合在一起

new[] 与 delete[] 行为与上类似

allocator类

很多时候并不需要立即在分配的内存上初始化一个对象,与此同时也希望在释放内存之前不调用对象的析钩函数;allocator类提供了这种机制,其成员:

allocator<T> a;   /* a可以用来分配原始内存以及在原始内存上构造T类型对象 */
a.allocate(n);     /* 分配原始的内存,可以保存n个Type类型的对象 */
a.deallocate(p,n);/* 释放p所指的原始内存,p必须合法;并且不会调用p所指对象的析钩函数,n表示p所指的内存可以保存n个T类型对象 */
a.construct(p,t);/* 在p所指的内存上调用T类型的复制构造函数(以t为参数)初始化一个对象 */
a.destory(p);/* 调用p所指对象的析钩函数 */

若干个用于在原始内存上构造对象的算法:


uninitialized_copy(begin,end,begin2);
/* 从迭代器[begin,end)指出的输入范围将元素复制到从迭代起begin2
 * 开始的未构造的原始内存中,该函数是在目的地构造对象,而不是赋值 */  
uninitialized_fill(begin,end,t)
/* 将由迭代器[begin,end)指出的范围中的对象初始化为t的副本
 * [begin,end)范围是未构造的原始内存,使用复制构造函数构造对象 */

uninitialized_fill_n(begin,end,t,n)
/* 同上,不过至多构造n个对象 */

operator new/operator delete

标准库提供了下列函数用于分配/释放原始的未构造的内存:

void* operator new(size_t)      /* new表达式调用 */
void* operator new[](size_t)    /* new[]表达式调用 */
void operator delete(void*)
void operator delete[](void*)

定位new表达式

用来在已经分配好的,原始的内存上调用构造函数初始化对象,与其他new表达式不同,定位new表达式不会分配新的内存,其语法:

/* 在已经分配的原始内存上调用对象的默认构造函数构造对象 */
new(原始内存指针) Type    
/* 根据参数类型与参数个数调用合适的构造函数构造对象 */
new(原始内存指针) Type(初始化参数)

#include <stdio.h>
#include <new>
#include <malloc.h>

class Test{
public:
	Test(){ printf("1\n"); }

	Test(int __a,int __b){ printf("2\n"); }

	~Test(){ ; }
};

int main(int argc,char *argv[]){
	Test *ptr=(Test*)malloc(sizeof(Test));
	new(ptr)Test;
	ptr->~Test();

	new(ptr)Test(3,3);
	ptr->~Test();
	free(ptr);
	return 0;
}


自定义new与delete

不能重定义new与delete表达式的行为,但是可以自定义new与delete表达式的行为
当编译器看到类类型的new或delete表达式时,她查看该类是否有operator new 或operator delete成员

  • 类定义(或继承)了自己的成员new和delete函数,则使用这些函数为对象分配/释放原始内存

  • 否则,调用这些函数的标准库版本

#include <stdio.h>
#include <new>
#include <malloc.h>

class Test{
public:
        void* operator new(size_t __size){
		printf("1\n");
		return ::operator new(__size);
	}
};


int main(int argc,char *argv[]){
	Test *test=new Test;
        delete test;    
        return 0;
}

自定义operator new与operator delete时只要注意参数类型返回值类型即可


#include <stdio.h>
#include <new>
#include <malloc.h>

class Test{
public:
	void operator delete(void* __p,size_t __size){
		printf("Size: %zd\n",__size);
		/* __size可有可无,若__size存在,编译器会把'delete p'
		 * p所指对象所占内存字节传递给__size */
		::operator delete(__p);
		return ;
	}
};


int main(int argc,char *argv[]){
	Test *test=new Test;
	delete test;
	return 0;
}


在类继承层次中自定义new与delete

对于operator new:


#include <stdio.h>
#include <new>
#include <malloc.h>

class A{
public:
	void* operator new(size_t __size){
		printf("A\n");
		return ::operator new(__size);
	}
};

class B:public A{
public:
	void* operator new(size_t __size){
		printf("B\n");
		return ::operator new(__size);
	}
};


int main(int argc,char *argv[]){
	A *a=new A;/* 调用A的operator new */
	A *b=new B;/* 调用B的operator new */
       /* 分析new表达式的行为->与 B *b=new B 是一样的情况 */ 
       delete a;
	delete b;
	return 0;
}

对于 operator delete,B继承A,当B中未定义operator delete时:



#include <stdio.h>
#include <new>
#include <malloc.h>

class A{
public:
	void operator delete(void *__p,size_t __size){
		printf("Size: %zd\n",__size);
		::operator delete(__p);
	}

	virtual ~A(){ ; }
};

class B:public A{
	int a;
};


int main(int argc,char *argv[]){
	A *a=new A;
	A *b=new B;
	delete a;/* a->A类型的对象,所以__size=sizeof(A) */
	delete b;
	/* 由于A中存在虚析够函数,所以编译器会将b所指动态类型的
	 * 尺寸传递给__size,即__size=sizeof(B) */
	return 0;
}

当B中也定义了operator delete时:



#include <stdio.h>
#include <new>
#include <malloc.h>

class A{
public:
	void operator delete(void *__p,size_t __size){
		printf("A:Size: %zd\n",__size);
		::operator delete(__p);
	}

	virtual ~A(){ ; }
};

class B:public A{
	int a;
	void operator delete(void *__p,size_t __size){
		printf("B:Size: %zd\n",__size);
		::operator delete(__p);
	}
};


int main(int argc,char *argv[]){
	A *a=new A;
	A *b=new B;
	delete a;	/* 调用A::operator delete */
	delete b;	/* 调用的是B::operator delete */
	return 0;
}

在delete b 中调用的 B::operator delete 有一点迷惑不解,由于 operator new()与operator delete()在类中默认是静态成员函数(因为当 A *a=new A时,分配内存会先于对象构造,所以应该是静态函数) 所以她们 不可能同时又是虚函数,所以delete b应该调用的是A::operator delete;但是确确实实调用的是B::operator delete;想来大概是编译器的作用,记住就行

自定义new[]/delete[]

与上非常相似,如不在类继承层次中的自定义:


#include <stdio.h>
#include <new>
#include <malloc.h>

class A{
public:
	A(){ printf("A\n"); }

	void* operator new[](size_t __size){
		printf("A:new Size: %zd\n",__size);
		return ::operator new[](__size);
	}

	void operator delete[](void *__p,size_t __size){
		printf("A:delete Size: %zd\n",__size);
		::operator delete[](__p);
		return ;
	}

	virtual ~A(){ printf("~A\n"); }
};

int main(int argc,char *argv[]){
	A *a=new A[4];
	delete[] a;/* 会把a所指内存的字节(即数组大小)传递给__size */
	return 0;
}

在类继承层次:



#include <stdio.h>
#include <new>
#include <malloc.h>

class A{
public:
	A(){ printf("A\n"); }

	void* operator new[](size_t __size){
		printf("A:new Size: %zd\n",__size);
		return ::operator new[](__size);
	}

	void operator delete[](void *__p,size_t __size){
		printf("A:delete Size: %zd\n",__size);
		::operator delete[](__p);
		return ;
	}

	void operator delete(void *__p,size_t __size){
		printf("A:delete Size: %zd\n",__size);
		::operator delete(__p);
		return ;
	}

	virtual ~A(){ printf("~A\n"); }
};

class B:public A{
	int b;
public:
	B():b(0){ printf("B\n"); }

	void* operator new[](size_t __size){
		printf("B:new Size: %zd\n",__size);
		return ::operator new[](__size);
	}

	void operator delete[](void *__p,size_t __size){
		printf("B:delete Size: %zd\n",__size);
		::operator delete[](__p);
		return ;
	}

	void operator delete(void *__p,size_t __size){
		printf("B:delete Size: %zd\n",__size);
		::operator delete(__p);
		return ;
	}

	virtual ~B(){ printf("~B\n"); }

};

int main(int argc,char *argv[]){
	A *b=new B;
	delete b;/* 正确释放 */

	B *b2=new B[4];
	delete[] b2;/* 可以正确释放 */

	A *b1=new B[4];
	delete[] b1;/* 此时提示段错误,编译器不能识别了... */
//      delete[] (B*)b1    /* 即可 */
        return 0;
}



注意 delete[] b1 出错!







易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!