为琐碎的对象在“ this”上调用新的展示位置是否安全?


20

我知道这个问题已经被问过好几次了,但是我找不到针对这种情况的答案。

假设我有一个琐碎的类,它不拥有任何资源,并且具有空的析构函数和默认构造函数。它具有少数带有类内初始化的成员变量;没有一个是const

我想重新初始化和此类对象,而无需deInit手动编写方法。这样做是否安全?

void A::deInit()
{
  new (this)A{};
}

我看不到任何问题-对象始终处于有效状态,this仍然指向同一地址;但是它是C ++,所以我想确定。


2
对象const有成员吗?
NathanOliver

2
如果这是有效的,那将等同于*this = A{};
凯文(Kevin)

2
@Amomum *this = A{};表示,this->operator=(A{});即创建一个临时对象并将其分配给*this,用临时值替换所有数据成员的值。因为这就是您想要的,而且(在我看来)比新的展示位置更具可读性,所以我会选择它。
凯文(Kevin)

1
@凯文哦,我不好,你是对的。比- 如果删除副本,我认为应该相等吗?
砂纸

1
与其用文字解释类,不如编写完整的类,而不仅仅是一种方法,要好得多。看到sscce.org
BЈовић

Answers:


17

与合法性相似 delete thisthis据我所知,,也允许将new放置到。此外,关于以后是否this可以使用或其他先前存在的指针/引用,也有一些限制:

[基本生活]

如果在对象的生存期结束之后并且在重新使用或释放​​该对象占用的存储空间之前,在原始对象占用的存储位置上创建了一个新对象,则指向原始对象的指针,引用原始对象,否则原始对象的名称将自动引用新对象,并且在新对象的生命周期开始后,可以用于操作新对象,如果:

  • 新对象的存储空间正好覆盖了原始对象所占据的存储位置,并且
  • 新对象的类型与原始对象相同(忽略顶级cv限定词),并且
  • 原始对象的类型不是const限定的,如果是类类型,则不包含任何类型为const限定的非静态数据成员或引用类型,并且
  • 原始对象和新对象都不是潜在重叠的子对象([intro.object])。

在此示例中,前两个条件是满意的,但后两个条件将需要考虑在内。

关于第三点,假定函数是非常量限定的,则假定原始对象是非常量应该是相当安全的。如果保持不变,则故障在调用方。关于const /引用成员,我认为可以断言这是可分配的:

static_assert(std::is_trivial_v<A> && std::is_copy_assignable_v<A>);

当然,由于可分配性是一项要求,因此您可以简单地使用*this = {};我希望产生相同程序的方法。一个可能更有趣的用例可能是为*this另一个类型的对象重用的内存(这将导致使用失败this,至少在没有重新解释+洗钱的情况下)。

与相似delete thisthis几乎无法将“ new ” 放置描述为“安全”。


有趣。是否可以通过某些类型特征使用static_assert确保满足所有这些条件?不确定是否有一个关于const成员的人……
Amomum

1
@Amomum我不认为可以成为子对象。const或reference成员会使该类不可分配,对于琐碎的类,我认为不会发生这种情况。
eerorika

这是否意味着严格混叠在const方面起作用?您能提供一个例子来说明这一点吗?
darune

1
@darune创建一个const对象,将其放置在新对象上,使用该对象的原始名称(或预先存在的指针或引用),其行为将是不确定的。如果不是完全相同,则其效果与严格的锯齿优化几乎相同。
eerorika

1
的倒数delete ptrnew T()。的倒数new(ptr)T{}ptr->~T();stackoverflow.com/a/8918942/845092
鸭子鸭

7

[basic.life] / 5中包含了解决此问题的规则

程序可以通过重用该对象占用的存储空间,或通过为类类型的对象显式调用析构函数来结束任何对象的生命周期。对于类类型的对象,在重用或释放对象占用的存储之前,不需要程序显式调用析构函数。但是,如果没有对析构函数的显式调用,或者没有使用delete-expression释放存储,则不会隐式调用析构函数,并且依赖于析构函数产生的副作用的任何程序均具有未定义的行为。

[basic.life] / 8

如果在对象的生存期结束之后并且在重新使用或释放​​该对象占用的存储空间之前,在原始对象占用的存储位置上创建了一个新对象,则指向原始对象的指针,引用原始对象,否则原始对象的名称将自动引用新对象,并且在新对象的生命周期开始后,可以用于操作新对象,如果:

  • 新对象的存储空间正好覆盖了原始对象所占据的存储位置,并且

  • 新对象的类型与原始对象相同(忽略顶级cv限定词),并且

  • 原始对象的类型不是const限定的,如果是类类型,则不包含任何类型为const限定的非静态数据成员或引用类型,并且

  • 原始对象和新对象都不是潜在重叠的子对象([intro.object])。

由于您的对象微不足道,因此您不必担心[basic.life] / 5,只要满足[basic.life] / 8的要点,那就很安全了。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.