是否可以使用placement-new来更改多态对象的类型?如果没有,标准中究竟禁止它的是什么?
考虑以下代码:
#include <new>
struct Animal {
Animal();
virtual ~Animal();
virtual void breathe();
void kill();
void *data;
};
struct Dead: Animal {
void breathe() override;
};
void Animal::kill() {
this->~Animal();
new(this) Dead;
}
呼叫“杀人”是否合法?
更新:早期的评论没有根据此处所示的编程技术标准来解决(il)合法性问题,即通过显式调用析构函数来更改对象的类型,并为兼容的新对象应用placement-new。 p>
因为有兴趣为什么有人愿意这样做,我可以提一下导致我提出这个问题的用例,尽管它与我提出的问题无关。
想象一下,您有一个具有多个虚拟方法的多态类型层次结构。在对象的生命周期中,发生的事情可以在代码中建模,因为对象改变了它的类型。有许多完美合法的方法来编程,例如,将对象保持为指针,智能与否,以及交换其他类型的副本。但这可能很昂贵:必须将原始对象克隆或移动到另一个不同类型的对象中才能交换新的对象。
在GCC,Clang和其他人中,更改对象的类型可以像简单地更改虚拟表指针一样便宜,但在便携式C ++中,除了通过构造新类型的对象之外,这是不可能的。
在我的原始用例中,对象也不能作为指针保存。
我想知道标准在重用记忆方面所说的内容。
答案 0 :(得分:2)
[basic.life] / 8 如果在对象的生命周期结束之后,在重用或释放对象占用的存储之前,在存储位置创建一个新对象,原始对象占用,指向原始对象的指针,引用原始对象的引用,或原始对象的名称将自动引用新对象,并且一旦新对象的生命周期开始,就可以用于操纵新对象,如果:
...
(8.4) - 原始对象是
T
类型的派生程度最高的对象(1.8),新对象是类型T
的派生程度最高的对象(也就是说,它们不是基类子对象)。
在您的示例中,对kill()
本身的调用可能有效(如果发生sizeof(Animal)==sizeof(Dead)
,我认为无法保证),但大多数尝试使用{{通过其进行调用的指针或Animal*
左值将通过访问其生命周期已结束的对象来触发未定义的行为。即使假设星对齐且Animal&
Animal
的{{1}}子对象完全覆盖原始独立Dead
对象的原始位置,也不会将此指针或左值视为指前者,但对于现已过期的后者。