请考虑下面的程序。它已经从一个复杂的案例简化了。除非我删除Obj类中的虚拟析构函数,否则它在删除先前分配的内存时失败。我不明白为什么程序输出中的两个地址不同,只有在虚拟析构函数存在的情况下。
// GCC 4.4
#include <iostream>
using namespace std;
class Arena {
public:
void* alloc(size_t s) {
char* p = new char[s];
cout << "Allocated memory address starts at: " << (void*)p << '\n';
return p;
}
void free(void* p) {
cout << "The memory to be deallocated starts at: " << p << '\n';
delete [] static_cast<char*> (p); // the program fails here
}
};
struct Obj {
void* operator new[](size_t s, Arena& a) {
return a.alloc(s);
}
virtual ~Obj() {} // if I remove this everything works as expected
void destroy(size_t n, Arena* a) {
for (size_t i = 0; i < n; i++)
this[n - i - 1].~Obj();
if (a)
a->free(this);
}
};
int main(int argc, char** argv) {
Arena a;
Obj* p = new(a) Obj[5]();
p->destroy(5, &a);
return 0;
}当虚拟析构函数存在时,这是我实现中程序的输出:
分配的内存地址从: 0x8895008开始,要释放的内存从: 0x889500c开始。
运行失败(退出值1)
请不要问它应该做什么。正如我所说的,它来自一个更复杂的案例,其中Arena是各种类型内存的接口。在本例中,内存只是从堆中分配和卸载。
发布于 2011-11-24 20:55:12
this不是new在char* p = new char[s];行返回的指针,您可以看到s的大小大于5个Obj实例。区别(应该是sizeof (std::size_t))是在附加内存中,包含数组的长度( 5 ),在紧接this中包含的地址之前。
好的,规范明确了:
http://sourcery.mentor.com/public/cxx-abi/abi.html#array-cookies
2.7阵列算子新Cookies
当使用运算符new创建一个新数组时,通常会存储cookie来记住分配的长度(数组元素的数量),以便能够正确地释放它。
具体地说:
如果数组元素类型T有一个平凡的析构函数(12.4 class.dtor),而通常的(数组)解分配函数(3.7.3.2 basic.stc.dynamic.deallocation)不带两个参数,则不需要cookie。
因此,析构函数的虚拟性是无关紧要的,重要的是析构函数是非平凡的,您可以很容易地检查它,方法是删除析构函数前面的关键字virtual并观察程序崩溃。
发布于 2011-11-24 21:28:08
根据寒冷的答案,如果你想让它“安全”:
#include <type_traits>
a->free(this - (std::has_trivial_destructor<Obj>::value ? 1 : 0));https://stackoverflow.com/questions/8262312
复制相似问题