第六章:new和delete

一、普通new运算符和delete运算符

  1.new运算符实际上由两个步骤组成:

  ①分配所需的内存:通过调用适当库的new运算符函数来分配内存(实际上所有new运算符都是由malloc完成,自己重载new时也应该用malloc分配内存,delete都由free完成)

  ②在分配的内存上建立对象或初始化(内置类型)

int *pi=new int(5);
//分解为以下两个步骤
int *pi=_new(sizeof(int));    //分配内存
*pi=5;                        //设置初始值

  2.delete运算符需要保证删除的内存有效

delete pi;
//转换为:
if(!pi)    _delete(pi);

  3.delete只是清除内存中的对象,但是指针本身依然有效,依然指向一个合法的地址,类似于void*指针。

  4.当需要以构造函数来配置对象时并且引入异常处理机制时,new和delete复杂一些

Point3d *origin=new Point3d;
//转换为
Point3d *origin;
//C++伪码
if(origin=_new(sizeof(Point3d))){
try{
    origin=Point3d::Point3d(origin);
}
catch(...){
    _delete(origin);           //删除已经分配的内存;
    throw;          //抛出原来的错误

delete origin;
//转换为
if(origin!=0){
try{
    Point3d::~Point3d(origin);}
catch{...}
{
    _delete(origin);
   throw;  
}}

  5.一般的library中的new操作符会在传入需要地址大小的参数size为空时,自动申请一个大小为1的地址空间并返回(为了保证能返回一个有效地址)并且还能让使用者提供一个自己的处理函数new_handle().

注:书上没讲具体操作,只是写了伪码,C++primer上说如果程序员要实现内存分配和构造对象分离的操作,那么内存分配通过new运算符实现(可以自己重载new运算符通过molloc函数),对象构建则通过定位new实现)。

 

二、针对数组的new和delete

  1.如果创建一个元素不含construct的数组,则不会调用vec_new(),因为只需要单纯的分配和释放内存。

  2.如果创建的数组其元素包含构造函数和析构函数则会调用vec_new()和vec_delete().

int *p_array=new int[5];
//转换如下
int *p_array=(int*)_new(5*sizeof(int));

Point3d *p_array=new Point3d[10];
//通常被编译为
Point3d *p_array;
p_array=vec_new(0,sizeof(Point3d),10,&Point3d::Point3d,&Point3d::~Point3d);

  3.删除一个数组的元素现在的做法是直接调用 delete[] p_array 不用在【】中加入数组大小(以前需要加入,现在加入数组大小被视为不良的做法)。

  因为现在的编译器①在new申请的内存区块中配置一个额外的word用来存放包含元素个数的包 或者 ②维护一个联合数组放置指针及大小。

  情况1当一个坏指针被交给delete_vec,会导致一个不正确的元素个数和起始地址(因为包改变了原本内存大小,所以一个错误的指针也没法指向正确的开头)。

  情况2当传入一个坏指针时,最多造成元素个数的错误。

  4.不要将一个基类指针指向派生类数组,否则就需要遍历数组自行进行析构函数的调用。

  

Point *ptr=new Point3d[10];
//调用自动的删除函数
delete [] ptr;
//此时传入vec_delete()函数的参数是Point的析构函数,其大小和Point3d不同,所以删除地址时,只有第一个元素的Point子对象被正确的清除。
//需要程序员手动清除
for(int ix=0;ix<elem_count;++ix)
{
    Point3d* p=&((Point3d*)ptr)[ix];
    delete p;
}

 

第六章:new和delete

上一篇:django缓存与信号


下一篇:ElasticSearch的深度分页