12.5 自由存储管理
1. 类 T 的任何空间分配函数都是静态成员(即使没有显式地声明为 static 也是静态的)。
2. 【例:
class Arena;
struct B {
void* operator new(size_t, Arena*);
};
struct D1 : B {
};
Arena* ap;
void foo(int i)
{
new (ap) D1; // 调用 B::operator new(size_t, Arena*)
new D1[i]; // 调用 ::operator new[](size_t)
new D1; // 非法 ::operator new(size_t) 被隐藏
}
】
3. 当使用 delete 表达式删除对象时,一个空间释放函数(对非数组对象来说是操作符 delete() ,对数组来说是操作符 delete[]() )会被隐式地调用,收回对象之前占据的空间。
4. 如果 delete 表达式是以单目操作符 :: 开头的,空间释放函数的名字会在全局域中进行查找。否则,如果 delete 表达式释放的对象的静态类型具有虚析构函数时,空间释放函数的名字会在该对象的动态类型(定义了虚析构函数)范围内查找。否则,如果 delete 表达式用来释放 T 类的对象或数组,该对象的动态和静态类型应该是一样的,空间释放函数会在 T 的范围内查找。如果查找失败,就到全局域中找。如果查找结果有二义性或者不可访问,或者查找结果选择了一个定点空间释放函数,程序就是非法的。
5. 执行 delete 表达式的时候,被选中的空间释放函数应当以待回收空间块的地址为第一个参数;如果使用 2 个参数的形式,块的大小作为第二个参数。
6. 类 X 的任何空间释放函数都必须是静态成员(即使没有显式地声明为 static 也是静态的)。【例:
class X {
// ...
void operator delete(void*);
void operator delete[](void*, size_t);
};
class Y {
// ...
void operator delete(void*, size_t);
void operator delete[](void*);
};
】
7. 由于分配和释放空间的成员函数都是静态函数,所以它们不能是虚函数。【注:在 delete 表达式中,由于空间释放函数实际上是在该对象的动态类型域中查找的,如果析构函数是虚函数,那么效果仍然是一样的。例如:
struct B {
virtual ˜B();
void operator delete(void*, size_t);
};
struct D : B {
void operator delete(void*);
};
void f()
{
B* bp = new D;
delete bp; // 1: uses D::operator delete(void*)
}
在这里,非数组的 D 类的对象被 D::operator delete() 释放空间,因为它具有虚析构函数。】【注:如果待释放的空间是一个对象数组的话,虚析构函数对实际决定调用哪个空间释放函数是不起作用的 。例如:
struct B {
virtual ˜B();
void operator delete[](void*, size_t);
};
struct D : B {
void operator delete[](void*, size_t);
};
void f(int i)
{
D* dp = new D[i];
delete [] dp; // 使用 D::operator delete[](void*, size_t)
B* bp = new D[i];
delete[] bp; // 未定义的行为
}
】
8. 空间释放函数的访问权限是静态检查的,因此,即使实际执行的是另外一个空间释放函数,静态查找找到的这个函数也应该具有访问权限。【例:上例中的 //1 句,如果 B::operator delete() 是 private 的,该 delete 表达式就是非法的。】