我试图理解这个奇怪的例子是如何工作的:
#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()
怎么可能?
答案 0 :(得分:3)
一个常见的误解是,无论何时取消引用NULL
指针,都会得到SIGSEGV
。这是不真实的。相反,你会得到未定义的行为。
特别是,如果您的代码取消引用指针,则允许编译器进行优化,就好像在那时不可能NULL
一样。在这里,唯一可以从NULL
中获取非dynamic_cast
指针的情况是它是否真的是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
答案 1 :(得分:2)
在这两种情况下,行为都是未定义的。您的“成功调用B::h()
”只是该未定义行为的特定表现。
您的第一个演员static_cast<B*>(ap1)
会立即产生未定义的行为。为了使此向下转换有效,指针ap1
必须指向某个A
对象的B
基础子对象。如果不是这种情况,则行为未定义。
你的第二个向下转换定义了行为,但在这些条件下它以dynamic_cast
失败的方式“失败”:它返回一个空指针。但是,您尝试通过该空指针调用bp2->h()
。在那个阶段,行为变得不明确。