当前位置: 首页 > 知识库问答 >
问题:

在放置新分配的对象时不调用析构函数可以吗?

文寒
2023-03-14

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

char *buffer; 

我使用新的布局在缓冲区中分配我的结构

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

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

我知道我应该手动调用这些分配项的析构函数,但如果没有涉及簿记/资源管理,可以忽略这个吗?换句话说,如果使用缓冲区的类的析构函数没有做任何事情(类似于上面的~S()),可以跳过这一步吗?如果是这样,我可以重用缓冲区而不破坏以前的租户吗?

共有3个答案

充修能
2023-03-14

除了Ben Voigt的答案详细说明了什么时候可以省略析构函数调用之外,重要的是要确保内存正确对齐,以便在其中放置新类型。我将尝试根据OP的要求将其写在这里。

这一行:

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

仅当地址缓冲区偏移量正确对齐时有效:

3.11对齐对象类型具有对齐要求(3.9.1、3.9.2),对该类型对象的分配地址进行了限制。对齐是一个实现定义的整数值,表示可以分配给定对象的连续地址之间的字节数
[…]

对于任何具有基本对齐要求的类型,缓冲区本身都正确对齐:

3.7.4.1分配函数
2[...]
返回的指针应适当对齐,以便它可以转换为具有基本对齐要求(3.11)的任何完整对象类型的指针,然后用于访问分配的存储中的对象或数组
[...]

要知道一个类型的对齐要求,有齐(type)。然后是std::max_align_t齐(std::max_align_t)返回所有具有基本对齐要求的类型的最大对齐值。

有一种特殊的类型需要扩展对齐,为了确保您的类型不是其中之一,我将在您的程序中包含它:

static_assert(alignof(S) <= alignof(std::max_align_t),  
              "Extended alignment required for S");

然后,您只需确保偏移量是对齐的倍数。

葛驰
2023-03-14

从技术上讲,不需要调用析构函数。实际上,安全总比抱歉好(一定要打电话给析构函数)

鲁波光
2023-03-14

本标准第3.8节[basic.life]中有一条规则,涵盖以下内容:

程序可以通过重用对象占用的存储,或通过显式调用具有非平凡析构函数的类类型的对象的析构函数来结束任何对象的生存期。对于具有非平凡析构函数的类类型的对象,在重用或释放该对象占用的存储之前,程序不需要显式调用析构函数;但是,如果没有显式调用析构函数,或者如果没有使用删除表达式(5.3.5)来释放存储,则不应隐式调用析构函数,并且依赖于析构函数产生的副作用的任何程序都具有未定义的行为。

许多专家一致认为,“取决于析构函数产生的副作用”过于含糊,没有用处。许多人将其解释为重言式,意思是“如果程序在未评估析构函数副作用时具有未定义的行为,则未能调用析构函数会导致未定义的行为”。看看可观察的行为和未定义的行为——如果我不调用析构函数会发生什么?

如果您的类型有一个平凡的析构函数(在您的示例中似乎是这样),那么调用它(或未能调用它)没有任何效果——调用平凡的析构函数甚至不会结束对象的生命。

当出现以下情况时,类型为T的对象的寿命结束:

  • 如果T是具有非平凡析构函数的类类型,则析构函数调用开始,或

也就是说,如果T没有非平凡的析构函数,那么结束对象o生存期的唯一方法就是释放或重用其存储。

 类似资料:
  • 我是C++的新手,我写了一个小程序来了解赋值如何处理对象。这个页面(http://www.cplusplus.com/doc/tutorial/classes2/)的cpp文档提示我这样做。在这一页上,它指出: 隐式版本[复制赋值操作符]执行浅层复制,这适用于许多类,但不适用于具有指向对象的指针的类,这些对象处理其存储。在这种情况下,不仅类会冒两次删除指向对象的风险,而且赋值会通过在赋值之前不删除

  • 更新1:根据建议添加了打印“this”。 更新2:拆分成几个文件,尝试阻止gcc优化。 更新3:记录复制构造函数并输入添加函数。 更新4:在main中添加了Clang和第二个cout的输出。 我希望参数析构函数作为函数中的最后一条语句被调用。从今以后,我希望下面的代码能够提供以下输出。 使用MSVC(Visual Studio)时,输出与预期的一样。但GCC(4.8.2-19ubuntu1)输出以

  • 问题内容: 在浏览一些旧代码时,我遇到了这个宝石: 第二行在Eclipse中被标记为无效代码,我知道为什么。似乎没有明确抛出任何异常,并且构造函数不可能抛出任何类型的异常(例如)。 我的问题是 为什么 要进行空检查?以前在Java的旧版本中,构造函数是否可以返回null?还是这仅仅是毫无用处的代码? 问题答案: 该代码在任何Java版本中均无效。构造函数不可能返回,即使构造函数会抛出异常,也不会调

  • 问题内容: 由于Java6周围的某个地方,热点JVM可以进行转义分析并在堆栈上而不是在垃圾收集堆上分配非转义的对象。这样可以加快生成代码的速度,并减少垃圾收集器上的压力。 Hotspot能够堆叠分配对象的规则是什么?换句话说,什么时候可以依靠它进行堆栈分配? 编辑 :该问题是重复的,但是(IMO)与原始问题相比,以下答案是一个更好的答案。 问题答案: 我做了一些实验,以查看Hotspot何时可以堆

  • 构造函数与析构函数是自动调用的。这些函数的调用顺序取决于执行过程进入和离开实例化对象范围的顺序。一般来说,析构函数的调用顺序与构造函数相反。但图6.9将介绍对象存储类可以改变析构函数的调用顺序。 全局范围中定义的对象的构造函数在文件中的任何其他函数(包括 main)执行之前调用(但不同文件之间全局对象构造函数的执行顺序是不确定的)。当main终止或调用exit函数时(见第18章)调用相应的析构函数