5

当您知道在这种特定情况下析构函数是 noop 时,标准是否要求调用非平凡的析构函数?

如果不调用析构函数,代码是否可能被编译器破坏?

用例是一个包含动态分配指针的类。默认情况下,这个指针是new在构造函数中获得的。此类还可以从分配器中获取其动态分配的指针。该类跟踪它是如何获得其指针的,delete如果指针是由分配器获得的,则在析构函数中调用,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();
}
4

1 回答 1

10

不。

对于具有非平凡析构函数的类类型的对象,在重用或释放对象占用的存储空间之前,程序不需要显式调用析构函数;但是,如果没有显式调用析构函数,或者如果没有使用delete-expression ( [expr.delete]) 来释放存储,则不应隐式调用析构函数,并且任何依赖于析构函数产生的副作用的程序具有未定义的行为.

[basic.life]

您不依赖于 的任何副作用~C,因此您没有未定义的行为。

注意你可能应该放置new[]A.Allocate的'dint[10]

C(Allocator& A) {
    ptr = new (A.Allocate(10 * sizeof(int))) int[10];
    need_cleanup = false;
}
于 2019-06-12T10:22:07.647 回答