65.9K
CodeProject 正在变化。 阅读更多。
Home

理解 Operator new[] 和 Operator delete[]

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.21/5 (42投票s)

2006年9月14日

CPOL

3分钟阅读

viewsIcon

83432

downloadIcon

196

了解用于管理数组内存的方法。

引言

内存管理在 C/C++ 中非常重要,因此是一个有趣的话题。在 C 中,存在 malloc 和 free。在 C++ 中,提供了 operator newoperator delete,它们可以提供更好的控制。

Operator newoperator delete 在内部调用相同的旧 malloc/free。但它们由 new 运算符和 delete 运算符调用,这些运算符调用对象构造函数和析构函数进行初始化和清理。

应避免混合使用 new/deletemalloc/free。 在这种情况下,结果是未定义的,而且很混乱。

operator new

此运算符在内部调用 malloc,并从内存初始化对象。 此运算符的简单形式是

static void* operator new(size_t size)
{
    FreeStore* p=(FreeStore*)malloc(sizeof(FreeStore));
    return p;
}

operator delete

此运算符在内部调用 free

static void operator delete(void *p)
{
     free(p);
}

Placement new

特殊的 operator new 不执行任何操作,用于从原始内存构造对象。它只是简单地实现为

  void *operator new (size_t, void *p)
   {
      return p;
   }

第一个不相关,但必须存在。并且此运算符返回传递给它的内存地址。 稍后,它用于对象构造。 您可以在 <new.h> 中看到此定义。

Placement delete

在一个类中,对于每种形式的 new,都应该存在相应的 delete。如果在您的类中定义了 operator placement new,但忘记定义相应的 placement delete,编译器会提醒您。placement delete 的一个简单形式是

static void operator delete(void*,void *)
{
 return;
}

operator new[] 和 operator delete[]

当为对象数组分配内存时,事情会变得有趣。考虑以下代码片段

FreeStore* pArrayOfObject=new FreeStore[1];
delete [] pArrayOfObject;

在上面的代码中,使用了 operator new[]operator delete[]。现在我们将讨论用于实现这些运算符的方法。 如何保持数组大小的跟踪。

使用的方法

有两种常用的方法来实现这一点

  1. 保留有关数组大小的信息,为此分配额外的内存。
  2. 保留一个全局映射,其中指针存储为键,其数组大小存储为值。

以下是如何实现它的简要说明。

1. 账本记录

如前所述,分配足够的内存来保存额外的信息,即数组的大小。 在删除该数组时,解开该内存并用于删除。 在我的示例中,我使用了 4 个字节来存储额外的信息。它声明为

const size_t BOOKKEEPING_SIZE=4;

FreeStore_BookKeep 用于演示这一点。

我使用了 static 方法 operator_new,该方法将被调用以分配元素数组,它接受参数 size_t,即要分配的元素数量。

memory allocation

此方法将像这样。

// here actual allocated size is, BOOKKEEPING_SIZE more than requested
size_t* ptmpObject = (size_t*) malloc(BOOKKEEPING_SIZE + nsize * 
					sizeof(FreeStore_BookKeep));
//keep size in to preceding buffer
*ptmpObject=size;
FreeStore_BookKeep* pThis = (FreeStore_BookKeep*) ( ptmpObject + BOOKKEEPING_SIZE) ;
size_t i(0);
//in case constructor throws exception, free allocated memory
for (i ; i < size; ++i)
{
    //initialize FreeStore_BookKeep object from raw memory
    new (pThis+i) FreeStore_BookKeep();
}
//returned memory is not  ptmpObject, which is originally allocated.
return pThis;

在上述方法中,分配的内存多于请求的内存。 为了删除此数组,使用了 static 方法 operator_delete,它以 void* 作为参数。 即,指向要删除的内存的指针。 此方法使用保存在额外字节中的相同信息来释放内存。 它的实现如下:

//to access size, need to look preceding memory area
size_t *ptemp=(size_t*)pMemoryToDelete-BOOKKEEPING_SIZE;
size_t size=(size_t)(*ptemp);
//need to call d'tor now
FreeStore_BookKeep *pThis=(FreeStore_BookKeep*)(pMemoryToDelete);
for (size_t i=0; i<size;i++)
{
    (pThis+i)->~FreeStore_BookKeep();
}
//Here free all memory allocated, considering book keeping
free(ptemp);

此方法的主要缺点是,如果用户不使用 delete[](在本例中为 operator_delete),而在指针上使用普通 delete,则永远不会释放 BOOKKEEPING_SIZE 字节数。

2. 使用全局映射

使用全局映射来存储有关指针和元素数量的信息,即,在分配内存时,将分配的指针存储为键,将元素数量存储为值,存储在全局映射中。 并在删除内存时,访问同一映射以查找已分配的元素数量。

FreeStore_Map 用于演示这一点。 全局映射声明为

typedef map<void*,size_t> MemoryInfo;
MemoryInfo gKeepSizeTrack;

新的 operator_new 方法将如下所示

FreeStore_Map *pThis = (FreeStore_Map*) malloc(nsize * sizeof(FreeStore_Map));
size_t i(0);
for (i ; i < nsize; ++i)
{
     //placement new just returns pointer passed ,
     //initializes objects from raw memory
     new (pThis+i) FreeStore_Map();
}
//save pointer in global map
gKeepSizeTrack[pThis]=nsize;
return pThis;

现在,在删除内存时,将访问同一映射。 新的 operator_delete 将如下所示

//look for global map
MemoryInfo::iterator it=gKeepSizeTrack.find(p);
//get size
size_t nsize=it->second;
FreeStore_Map* pTemp=(FreeStore_Map*)p;
for (size_t i=0; i<nsize;i++)
{
    (pTemp+i)->~FreeStore_Map();
}
//remove from global map
gKeepSizeTrack.erase(p);
//free memory allocated
free(p);

operator_newoperator_delete 本质上需要是 static 的。 因为运算符 new 是初始化/构造调用构造函数的类的方法,而运算符 delete 销毁对象,因此这些方法本质上需要是 static 的。

使用此代码

提供的代码的主要功能如下所示

//array of 100 elements using Book keep method
FreeStore_BookKeep *pObjArr=FreeStore_BookKeep::operator_new(100);
FreeStore_BookKeep::operator_delete(pObjArr);//delete it
//array of 200 elements using global map method
FreeStore_Map *pObjArr1=FreeStore_Map::operator_new(200);
FreeStore_Map::operator_delete(pObjArr1);//delete it

参考文献

  • MSDN 文章 "处理异常,第 5 部分",作者:Robert Schmidt

历史

  • 2006 年 9 月 14 日:发布
  • 2006 年 9 月 17 日:更新
  • 2006 年 9 月 21 日:更新
© . All rights reserved.