在C++中,分配和归还内存时需要保持数组和非数组形式的操作符的匹配:
T *aT = new T; // 非数组
T *arrT = new T[16]; // 数组
delete aT; // 非数组
delete [] arrT; // 数组
aT = new T[1]; // 数组
delete aT; // 错误!应该采用数组形式的操作符
保持这些函数正确地成对使用很重要,因为数组的分配和归还所使用的函数不同于非数组形式。对于数组而言,new表达式不是使用operator new为数组分配存储区,而是使用array new。类似地,delete表达式也不是调用operator delete来释放数组的存储区,而是调用array delete。更确切的说,当分配一个数组时应该使用一个不同的操作符,即new[],而不是像分配一个非数组的对象那样使用new操作符。规划内存的情形类似。
array new和array delete分别是operator new和operator delete数组版本的对应物,它们的声明方式类似:
void* operator new(size_t) throw(bad_alloc); // operator new
void* operator new[](size_t) throw(bad_alloc); // array new
void operator delete(void*) throw(bad_alloc); // operator delete
void operator delete[](void*) throw(bad_alloc); // array delete
有关这些函数的数组形式最常出现的混乱情形,出现于一个特定的类或类层次结构使用成员operator new和operator delete定义了自己的内存管理方式时:
class Handle
{
public:
// ...
void* operator new(size_t);
void operator delete(void*);
// ...
};
Handle类定义了非数组形式的内存管理函数,但它们并不被用于Handle数组的情形,对于数组的情形,调用的是全局array new和array delete:
Handle* arrHandle = new Handle[MAX]; // 调用::operator new[]
// ...
delete [] arrHandle; // 调用::operator delete[]
从逻辑上来说,只要声明了非数组形式的函数(即operator new和operator delete),就应该为这些函数声明数组的形式,在日常编程实践中,这一点往往被忽视。如果目的是想调用全局的数组分配操作,那么,定义“仅仅转发对全局形式的调用”的局部形式,可以让事情变得更清晰:
class Handle
{
public:
// ...
void* operator new(size_t);
void operator delete(void*);
void* operator new[](size_t n)
{
return ::operator new(n);
}
void operator delete[](void* p)
{
::operator delete(p);
}
// ...
};
如果目的是禁止分配Handle数组,那么可以将数组形式的函数声明为private并且不提供定义。
另外一个易产生混乱和错误的地方在于,传递给array new的那个表示大小的参数值,取决于函数是如何被调用的。当operator new在一个new表达式中被隐式地调用时,编译器会决定需要多少内存,并将该数量作为参数传递给operator new。这个数量就是正在分配的对象的大小:
aT = new T; // 调用operator new(sizeof(T());
也可以直接调用operator new,在这种情况下,必须明确指明希望分配的字节数:
aT = static_cast<T*>(operator new(sizeof(T)));
也可以直接调用array new:
arrT = static_cast<T*>(operator new[](sizeof(T) * 16));
然而,当通过new表达式隐式地调用array new时,编译器常常为略微增加一些内存请求:
arrT = new T[16]; // 请求内存为* sizeof(T) + delta字节
所请求的额外空间一般由运行期内存管理器(runtime memory manager)来记录关于数组的一些信息,这些信息(包括分配的元素个数、每个元素的大小等)对于以后回收内存是必不可少的。不过,事情远没有这么简单,编译器未必为每一个数组分配都请求额外的内存空间,并且对于不同的数组分配而言,额外请求的内存空间大小也会发生变化。
请求内存数量上的区别通常只有在编写非常底层的代码时才需要考虑,在这种情形下,数组的存储区被直接处理。如果打算编写底层代码,通常最简单的做法是避免直接调用array new以及编译器所执行的有关干预,取而代之的是,使用普通的operator new。
posted on 2011-06-29 08:47
水 阅读(2860)
评论(0) 编辑 收藏 引用 所属分类:
c/c++基础知识