当您知道在这种特定情况下析构函数是noop时,标准是否要求调用非平凡的析构函数?
如果不调用析构函数,代码是否有可能被编译器破坏?
用例是一个包含动态分配的指针的类。默认情况下,此指针由构造函数中的new
获取。此类也可以从分配器获取其动态分配的指针。该类会跟踪其获取指针的方式,如果指针是由delete
获取的,则在destrucor中调用new
;如果指针是由分配器获取的,则不会调用它,因为分配器将释放内存。动态内存中存储的数据只是微不足道的类型,因此不需要调用它们的析构函数。
所以问题是,如果我知道它是通过分配器获得的指针,那么析构函数是noop的话,我是否仍需要在该类上调用析构函数?
这是一个最小的简化示例,与该问题没有直接关系的所有内容都已删除。
struct Allocator {
void* ptr = nullptr;
void* Allocate(size_t size) {
ptr = malloc(size);
return ptr;
}
~Allocator() { // allocator will cleanup
if (ptr)
free(ptr);
}
};
struct C {
int* ptr;
bool need_cleanup;
C() {
ptr = new int[10];
need_cleanup = true;
}
C(Allocator& A) {
ptr = (int*)A.Allocate(10 * sizeof(int));
need_cleanup = false;
}
~C() { // non-triviall because user-defined.
if (need_cleanup)
delete[] ptr;
// noop if need_cleanup is false.
}
};
int main()
{
Allocator A;
alignas(C) char buffer[sizeof(C)];
C* c = new(buffer) C(A);
/// is it required to call c->~C();
}
答案 0 :(得分:9)
否。
对于具有非平凡析构函数的类类型的对象,在重用或释放该对象占用的存储空间之前,不需要程序显式调用析构函数。但是,如果没有显式调用析构函数,或者不使用delete-expression(
[expr.delete]
)来释放存储,则不应隐式调用析构函数,并且任何依赖于副作用的程序析构函数产生的行为不确定。
您不依赖于~C
的任何副作用,因此您没有不确定的行为。
您应该将new[]
的{{1}}放置到A.Allocate
int[10]