在使用placement new分配的对象上,不调用析构函数是否可以?

8

假设我有一个固定的内存缓冲区

char *buffer; 

我使用放置 new 运算符在那个缓冲区分配我的结构体。

struct S
{ 
    std::tuple<int, double, char> m_data; 
    auto getRecord() 
    { 
        return m_data;
    }
};

S *newS = new(buffer + offset)S; 

我知道对于这样分配的项,我应该手动调用析构函数,但是如果没有涉及到簿记/资源管理,是否可以省略此步骤?换句话说,如果使用缓冲区的类的析构函数什么也不做(类似于上面的~S()),那么可以跳过此步骤吗?如果是这种情况,我可以在不销毁以前的租户的情况下重复使用缓冲区吗?


1
如果您不需要进行任何资源管理,为什么一开始就要有一个析构函数呢? - Barmar
@Barmar,它将由编译器自动生成一个。 - Lorah Attkins
4
请注意buffer + offset的地址对齐。 - alain
1
@Lorah:你必须确保指针 buffer + offset 对于你的类型是正确对齐的。如果 buffer 本身没有对齐,或者混合使用类型导致 offset 破坏了对齐,则可能会出现问题。 - Ben Voigt
通常情况下,如果:(1)您不真正需要它或(2)您不了解它的工作原理,则自己进行内存管理并不是一个好主意。 - Phil1970
显示剩余6条评论
3个回答

18
标准在第3.8节[basic.life]中规定了以下规则:
一个程序可以通过重复使用对象所占用的存储空间或显式调用具有非平凡析构函数的类类型对象的析构函数来结束任何对象的生命周期。对于具有非平凡析构函数的类类型对象,程序在重复使用或释放对象所占用的存储空间之前不需要显式调用析构函数;但是,如果没有显式调用析构函数或者没有使用delete表达式(5.3.5)来释放存储空间,则析构函数不得被隐式调用,且任何依赖析构函数产生的副作用的程序都具有未定义行为。
许多专家都认为,“依赖析构函数产生的副作用”太过模糊,无法提供实用价值。许多人将其解释为循环论证,即“如果程序在不评估析构函数副作用时具有未定义行为,则未调用析构函数会导致未定义行为”。请参见可观察行为和未定义行为——如果我不调用析构函数会发生什么? 如果您的类型具有平凡析构函数(在您的示例中似乎是这种情况),那么调用它(或不调用它)根本没有任何影响——调用平凡析构函数甚至不能结束对象的生命周期。
因此,如果T没有非平凡析构函数,则结束对象o的生命周期的唯一方法是释放或重复使用其存储空间。

如果所有元组类型都是平凡的,则我猜tuple有一个平凡的析构函数。 - M.M

5

从技术上讲,不需要调用析构函数。但实际情况是,安全起见最好调用析构函数(确保调用析构函数)。


1
在维护过程中,某人可能会向 S 添加一个析构函数...除非他遇到了严重的性能问题并且分析表明内存管理是原因,并且可以实现显着的改进,否则没有人应该编写这种代码。 - Phil1970

4
除了Ben Voigt回答的详细说明何时可以省略析构函数调用之外,确保所要放置的类型在其中进行new分配的内存正确对齐也很重要。我将尝试在此处按照OP的要求撰写:(参见这里)
这一行:
S *newS = new(buffer + offset)S;

只有当地址buffer+offset对齐时才有效:
3.11 对齐
对象类型具有对齐要求(3.9.1,3.9.2),这些要求限制了该类型对象可以分配在哪些地址上。对齐是一个实现定义的整数值,表示给定对象可以分配的连续地址之间的字节数。 buffer本身对于任何具有基本对齐要求的类型都是正确对齐的:
3.7.4.1 分配函数
返回的指针应该适当地对齐,以便它可以被转换为任何具有基本对齐要求(3.11)的完整对象类型的指针,然后用于访问所分配存储中的对象或数组。
要知道类型的对齐要求,可以使用alignof(type)。然后有std::max_align_talignof(std::max_align_t)返回所有具有基本对齐要求的类型中最大的对齐值。
还有一种特殊情况,需要扩展对齐,为了确保您的类型不属于这些类型之一,我建议将此包含在您的程序中:
static_assert(alignof(S) <= alignof(std::max_align_t),  
              "Extended alignment required for S");

然后,您只需确保 offsetalignof(S) 的倍数即可。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接