当可动型的称为对象变化?(When may the dynamic type of a referr

2019-09-23 12:42发布

让我们用一个例子开始:

#include <cstdio>

struct Base { virtual ~Base() {} virtual void foo() = 0; };
struct P: Base { virtual void foo() override { std::printf("Hello, World!"); } };
struct N: Base { virtual void foo() override {} };

void magic(Base& b);
// Example implementation that changes the dynamic type
// {
//     void* s = dynamic_cast<void*>(&b);
//     b.~B();
//     new (s) N();
// }

int main() {
    std::aligned_storage<sizeof(P), alignof(P)> storage;
    void* s = static_cast<void*>(storage);

    new (s) P();

    Base& b = *static_cast<Base*>(s);

    magic(b);

    b.foo();
 }

什么,按照标准,应该b.foo()打印?

个人观点:这是不确定,因为b后,我们在销毁实例了陈旧的magic 在这种情况下,会替换b.foo()通过static_cast<B*>(s)->foo()使这合法吗?


所以,现在我们有一个例子,可能(或不)是合法的,在眼前对我们所有人来说standardistas更普遍的问题是,是否改变了动态类型的对象是都不放过。 我们已经知道,C ++编译器可重复使用的存储(幸运的),所以这是一个有点棘手。

这个问题似乎是理论上的,但是它有编译器直接应用:可以将编译器devirtualize b.foo()bP::foo()在上面的程序?

因此,我在寻找:

  • 关于我自己的小程序一个明确的答案(我不能想出一个)。
  • 一个可能的例子(单个就足够了)的改变的动态类型的对象的一合法的方式。

Answer 1:

根据标准的§8.5.3.2,参考不能被结合到初始化后另一个对象。 由于放置new创建了一个新的对象,你违反了这个规则,并得到了一个未定义的行为。

动态类的一个对象不能改变。 即使在你的榜样,你不改变对象的类型,但创建在同一个地方作为一个老一个新的不同的对象。 如果你想想看,改变了动态类型的对象将意味着调整的对象就地容纳额外的数据成员和改变VMT(然后将其他移动物体和搞砸了指针......),它可以不语言的规则内进行。



Answer 2:

这是不确定的行为 。 你的magic例如违反参考的语义。

此外, dynamic_cast向下 -casting。 投射到void*static_cast

要明确地回答你的问题:

  • 编译器可能会“devirtualize”任何函数调用它喜欢,如果能证明运行时类型。
  • 如果参考会超越它指的对象,它的UB。
  • 你不能改变的动态类型的对象,你可以做的是重新分配的指针最接近。

     Base * ptr; P p; N n; ptr = &p; ptr -> foo (); ptr = &n; ptr -> foo (); 

但是, pn是固定式的,直到他们走出去的范围(或者,如果在堆中分配,当他们delete d)。



文章来源: When may the dynamic type of a referred to object change?