是noop时是否需要调用非平凡的析构函数?

时间:2019-06-12 10:03:37

标签: c++ memory-management language-lawyer lifetime explicit-destructor-call

当您知道在这种特定情况下析构函数是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();
}

1 个答案:

答案 0 :(得分:9)

否。

  

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

[basic.life]

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

您应该将new[]的{​​{1}}放置到A.Allocate

int[10]