重载new[]操作符的问题?
自已编写了一个内存分配管理器,并在每个class中与struct中都加入了new操作符的重载声明(delete也有)。
typedef struct test
{
....
void *operator new( size_t cb ){.....};
void *operator new[]( size_t cb ){.....};
....
}
TEST;
class A
{
....
void *operator new( size_t cb ){.....};
void *operator new[]( size_t cb ){.....};
....
};
当:
TEST *p = new TEST;
与
A *p_a = new TEST;
这两个的返回是正确的,但是:
TEST *p = new TEST[3]; //这个返回的地址正确
A *p_a = new A[3];//这个返回的地址不正确
class使用new[]申请时或多或少会在已申请到的地址(自己编写的分配地址函数的返回值)上多返回4获4的倍数字节数。使得在使用delete[]的时侯不能正确在内存分配表中查找出用new[]分配的地址。
我想问这个是由于什么原因引起的?
[解决办法]
可能是因为class A 有non-trivial的析构函数引起的.
因为在释放内存时,必须对数组的每个元素调用析构函数,所以,编译器需要在分配的内存上记录数组的个数,按照记录位置的不同,可能对operator new[],operator delete[]的写法隐含潜在需求.
例如,new A[2]时,某编译器可以这样做:
要求分配sizeof(size_t) + 2*sizeof(A)的内存,假设operator new[]返回地址为 X. 编译器在X处存放数组长度2(占用sizeof(size_t)字节),并在X+sizeof(size_t), X + sizeof(size_t) + sizeof(A)处构造对象,然后返回给用户地址X+sizeof(size_t),这就是 new A[2] 的返回值,它是数组元素的首地址,但不是所分配内存块的首地址.
在调用delete时,用户传递给编译器的地址是X+sizeof(size_t),编译器向前取得数组长度,从而可以对每个数组元素调用析构.最后,将地址X传递给operator delete[].所以,请注意此时operator delete[]得到的仍然是所分配内存的首地址,没有任何问题.(但是,也有可能传递X+sizeof(size_t),我认为这是不好的,但是不能排除这种可能性.因此,需要实验.如果是后者,operator delete[]需要通过计算获得X).
以上只是一种实现动态分配数组的模式.无论多分配的字节长度,它的位置,地址传递的方式等等都是不确定的.事实上,编译器可以用任意的方式实现动态分配数组.所以,需要根据从operator new[]返回的地址,从new A[2]获得的地址和传递给operator delete[]的地址这三者推断出编译器所采取的模式,从而做出正确的反应.无论如何,从operator new[]返回的地址和从new A[2]获得的地址可以不一样,这很正常,因为编译器在这两者之间做了许多隐蔽的事.