(五)new和delete

new的实现

Complex* pc = new Complex(1, 2);

//编译器转化为三步
void* mem = operator new(sizeof(Complex)); //分配内存
pc = static_cast<Complex*>(mem);		   //将void指针转换为所需类型的指针
pc->Complex::Complex(1, 2);				   //构造函数 
  • 第一步调用operator new,内部调用malloc(n),分配所需大小的内存。指针指向分配内存的起始位置。
  • 第二部将void*指针转换为所需类型的指针
  • 第三步在指针所指的内存中调用构造函数创建所需的对象

delete的实现

delete pc;

//编译器转化为两步
Complex::~Complex(pc);      //析构函数
operator delete(pc);		//释放内存
  • 第一步对应析构函数
  • 第二步调用operator delete,内部调用free(ps),释放内存
  • 在上例中一共有两个删除操作,第一次是调用析构函数释放它动态分配的内存,第二次释放动态分配ps所指的内存

new[ ]与delete[ ]要搭配使用

  • delete[ ]会表示要删除一个数组,要调用多次析构函数,最后再释放new[ ] 申请的空间,不会发生内存泄漏。
  • delete表示要删除一个元素,只会调用一次析构函数,如果申请的数组中每个元素均有一个动态分配的空间,只调用一次析构函数会导致其余元素申请的空间发生内存泄漏,而不是申请数组的空间发生内存泄漏。

重载operator new、operator delete、operator new[ ]、operator delete[ ]

全局范围内的重载
void* myAlloc(size_t size)
{ return malloc(size); }
void myFree(void* ptr)
{ return free(ptr);}


//它们不可以被声明于一个namespace中
inline void* operator new(size_t size)
{ cout<< "global new() \n"; return myAlloc(size);}

inline void* operator new[](size_t size)
{ cout<< "global new[]() \n"; return myAlloc(size);}

inline void operator delete(void* ptr)
{ cout<< "global delete() \n"; myFree(ptr);}

inline void operator delete[](void* ptr)
{ cout<< "global delete[]()\n"; myFree(ptr);}
  • operator new一定要传入一个参数表示大小,这个是由编译器传进来的
  • operator delete一定要传入一个指针,表示要释放的地址
  • 这四个函数的重载是全局函数,它的影响巨大
类内的重载
class Foo{
public:
	void* operator new(size_t);
	void operator delete(void*, size_t);//size_t是可选的参数,传入一个参数也可以
	//...
};
  • new分解为三个动作,delete分解为两个动作,这样可以用自定义的操作符接管内存的分配和释放行为
  • 接管的动作可以做一个内存池
Foo* p = new Foo;
/*----------------------new分解为三步--------------------------
try{
	void* mem = operator new(sizeof(Foo));
	p = static_cast<Foo*>(mem);
	p->Foo::Foo();
}
*/--------------------------------------------------------------
delete p;
/*----------------------delete分解为两步---------------------
p->~Foo();
operator delete(p);
*/-----------------------------------------------------------------

  • 重载member operator new [ ] / delete[ ]
class Foo{
public:
	void* operator new[](size_t);
	void operator delete[](void*, size_t);//size_t是可选参数
	//...
}
Foo* p = new Foo[N];
/*----------------------------new[]分解为三步---------------------
void* mem = operator new(sizeof(Foo)*N + 4);//vc中数组会用4个字节储存数组的大小
p = static_cast<Foo*>(mem);
p->Foo::Foo;//N次
*/----------------------------------------------------------------
...
delete[] p;
/*------------------------------delete[]分解为两步--------------------
p->~Foo();//N次
operator delete(p);
*/-----------------------------------------------------------------
  • 无视类内的重载,调用全局的operator new
  • new调用operator new[ ]时传入的大小,不但包含N*每个元素的大小,还包括一个计数器,记录数组一共有几个元素
  • 构造函数是从头到尾构造每一个元素的,析构函数是从尾到头释放每一个元素
Foo* pf = ::new Foo;// void* ::operator new(size_t);
::delete pf;//void ::operator delete(void*)
  • 重载class member operator new(),且可以重载很多个版本,前提是每一版本都有独一无二的参数列,其中第一参数必须是size_t,其余参数以new所指定的placement arguments为初值
  • 出现于new(…)小括号内的便是所谓的placement arguments
Foo* pf = new(300, 'c')Foo;

在这里插入图片描述

  • 也可以重载class member operator delete(),写出多个版本。但它们绝不会被delete调用

  • 只有当调用operator new()后调用构造函数时抛出异常,即构造没有成功,才会调用这些重载版本的operator delete()

  • 它们只可能这样被调用,主要用来完成未能完全创建成功的对象所占用的内存
    在这里插入图片描述

  • 即使operator delete(…)未能与operator new(…)一一对应,也不会有任何报错。因为operator delete(…)只会在抛出异常时被调用,编译器认为没有定义表示不在乎异常

  • 标准库对placement new的示例

  • create中调用operator new(extra)表示分配一个Rep加上extra,为的是可以增加一个引用计数的功能,Rep表示计数器,即有多少一起共享该字符串

  • 当想实现多分配一些东西时可以使用operator new(…)
    在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值