奇怪的交叉播.这怎么可能

Strange cross-cast. How is it possible?

本文关键字:怎么可能      更新时间:2023-10-16

我试图了解这个奇怪的示例如何工作:

#include <iostream>
class A {
    public:
        virtual void f() {std::cout << "A::f()" << std::endl;}
};
class B : public A {
    public:
        void f() {std::cout << "B::f()" << std::endl;}
        void h() {std::cout << "B::h()" << std::endl;}
};
class C : public A {
    public:
        void f() {std::cout << "C::f()" << std::endl;}
};
int main()
{
    A* ap1 = new C();
    auto bp1 = static_cast<B*>(ap1);
    bp1->h();
    A* ap2 = new C();
    auto bp2 = dynamic_cast<B*>(ap2);
    bp2->h();
    return 0;
}

由于两种铸造的结果,它成功地称为B :: H()。完整输出为:

B::h()
B::h()

怎么可能?

一个常见的误解是,每当您取消NULL指针时,都会得到SIGSEGV。这是不真实的。相反,您会得到未定义的行为。

尤其是,如果您的代码删除指针,则允许编译器优化,好像当时不可能是NULL。在这里,唯一可以从dynamic_cast中获取非NULL指针的情况是,如果它确实是B的实例,因此编译器消除了检查。

-fsanitize=undefined编译以获得有意义的结果:

ub.cpp:22:35: runtime error: downcast of address 0x55c9736dcc20 which does not point to an object of type 'B'
0x55c9736dcc20: note: object is of type 'C'
 00 00 00 00  48 fd c3 71 c9 55 00 00  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  21 00 00 00
              ^~~~~~~~~~~~~~~~~~~~~~~
              vptr for 'C'
ub.cpp:23:11: runtime error: member call on address 0x55c9736dcc20 which does not point to an object of type 'B'
0x55c9736dcc20: note: object is of type 'C'
 00 00 00 00  48 fd c3 71 c9 55 00 00  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  21 00 00 00
              ^~~~~~~~~~~~~~~~~~~~~~~
              vptr for 'C'
B::h()
ub.cpp:27:11: runtime error: member call on null pointer of type 'struct B'
ub.cpp:27:11: runtime error: member access within null pointer of type 'struct B'
[1]    10583 segmentation fault (core dumped)  ./ub

在两种情况下,行为均未定义。您的"成功调用B::h()"只是该不确定行为的特殊表现。

您的第一个铸造static_cast<B*>(ap1)本身就会立即产生未定义的行为。为了使此降低是有效的,指针ap1必须指向某些B对象的A基本子对象。如果不是这种情况,则行为是不确定的。

您的第二个降低的行为定义了行为,但是在这些条件下,它"失败"了dynamic_cast失败的方式:它返回一个空指针。但是,您尝试通过该空指针调用bp2->h()。在那个阶段,行为变得不确定。