Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/143.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 在放置新分配的对象时不调用析构函数可以吗?_C++ - Fatal编程技术网

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

C++ 在放置新分配的对象时不调用析构函数可以吗?,c++,C++,假设我有一个固定的内存缓冲区 char *buffer; 我使用placement new在该缓冲区中分配我的结构 struct S { std::tuple<int, double, char> m_data; auto getRecord() { return m_data; } }; S *newS = new(buffer + offset)S; 结构 { std::元组m_数据; 自动获取记录() { 返

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

char *buffer; 
使用placement new在该缓冲区中分配我的结构

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

S *newS = new(buffer + offset)S; 
结构
{ 
std::元组m_数据;
自动获取记录()
{ 
返回m_数据;
}
};
S*newS=新(缓冲区+偏移量)S;

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

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

从技术上讲,不需要调用析构函数。实际上,安全比抱歉好(请叫析构函数)

标准在第3.8节
[basic.life]
中有一条规则,其中包括:

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

许多专家一致认为,“取决于毁灭者产生的副作用”太模糊了,没有用处。许多人将其解释为重言式,意思是“如果程序在未评估析构函数副作用时有未定义的行为,那么未能调用析构函数将导致未定义的行为”。看

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

类型为
T
的对象
o
的寿命在以下情况下结束:

  • 如果
    T
    是具有非平凡析构函数的类类型,则析构函数调用将启动,或者
  • 对象占用的存储被释放,或被未嵌套在
    o
    中的对象重用

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

本标准第3.8节
[basic.life]
中有一条规则,其中包括:

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

许多专家一致认为,“取决于毁灭者产生的副作用”太模糊了,没有用处。许多人将其解释为重言式,意思是“如果程序在未评估析构函数副作用时有未定义的行为,那么未能调用析构函数将导致未定义的行为”。看

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

类型为
T
的对象
o
的寿命在以下情况下结束:

  • 如果
    T
    是具有非平凡析构函数的类类型,则析构函数调用将启动,或者
  • 对象占用的存储被释放,或被未嵌套在
    o
    中的对象重用

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

除了Ben Voigt的答案,该答案详细说明了何时可以省略析构函数调用,确保内存正确对齐,以便将新的类型放入其中,这一点很重要。我会尽量把它写在这里

这一行:

S *newS = new(buffer + offset)S;
仅当地址
缓冲区+偏移量
正确对齐时工作:

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

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

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

要了解类型的对齐要求,有
alignof(type)
。然后是
std::max\u align\u t
alignof(std::max\u align\u t)
static_assert(alignof(S) <= alignof(std::max_align_t),  
              "Extended alignment required for S");