所以我正在尝试更多地了解 C-style-casts , static_cast 之间的差异, dynamic_cast 我决定尝试这个应该反映 C-style-casts 之间差异的示例和 static_cast 非常好。
class B
{
public:
void hi() { cout << "hello" << endl; }
};
class D: public B {};
class A {};
int main()
{
A* a = new A();
B* b = (B*)a;
b->hi();
}
这段代码片段应该反映 C-style-cast 非常错误,并且根本检测不到错误的演员表。部分地发生这种情况。没有检测到糟糕的演员阵容,但是我很惊讶程序,而不是在b->hi();
崩溃,它在屏幕上打印出“你好”这个词。
现在,为什么会这样?当没有实例化B对象时,用什么对象来调用这样的方法?我正在使用g ++进行编译。
答案 0 :(得分:3)
正如其他人所说,这是不确定的行为。
为什么它有效?这可能是因为函数调用在编译时是静态链接的(它不是虚函数)。函数B::hi()
存在,因此被调用。尝试将变量添加到class B
并在函数hi()
中使用它。然后你会在屏幕上看到问题(垃圾值):
class B
{
public:
void hi() { cout << "hello, my value is " << x << endl; }
private:
int x = 5;
};
否则你可以使函数hi()
成为虚拟的。然后函数在运行时动态链接,程序立即崩溃:
class B
{
public:
virtual void hi() { cout << "hello" << endl; }
};
答案 1 :(得分:1)
现在,为什么会发生这种情况?
因为它可能发生。任何事情都可能发生。行为未定义。
事情发生了意想不到的事实,说明为什么UB如此危险。如果它总是导致崩溃,那么处理起来会容易得多。使用什么对象来调用这样的方法
最有可能的是,编译器盲目地信任您,并假设b
确实指向类型为B
的对象(它没有)。它可能会使用指向的内存,就好像假设是真的一样。成员函数不访问属于该对象的任何内存,并且行为恰好与存在正确类型的对象相同。
未定义,行为可能完全不同。如果你试图再次运行程序,标准并不能保证恶魔不会飞出你的鼻子。
答案 2 :(得分:1)
这只能起作用,因为<DataTemplate DataType="{x:Type local:MyViewModel}">
<!-- xaml is typed here directly -->
<Border>
...
</Border>
</DataTemplate>
方法本身的实现,以及C ++规范的特殊部分称为未定义的行为。
使用C样式转换为不兼容的指针类型进行转换是未定义的行为 - 实际上任何事情都可能发生。
在这种情况下,编译器显然决定只信任你,并决定相信hi()
确实是指向b
实例的有效指针 - 这实际上都是C -style cast将会执行,因为它们不涉及运行时行为。当您在其上调用B
时,该方法可以正常运行,因为:
hi()
但不访问B
的任何实例变量(实际上,它根本不访问任何实例变量)A
的vtable中查找因此它可行,但在几乎所有非平凡的情况下,这样的格式错误的强制转换后跟方法调用会导致崩溃或内存损坏。而且你不能依赖这种行为 - undefined 并不意味着每次运行它都必须是相同的。编译器完全在其权利范围内,使用此代码插入随机数生成器,并在生成1时,启动原始 Doom 的完整副本。每当涉及未定义行为的任何事情似乎都有效时,请牢记这一点,因为明天可能无法正常工作,您需要像这样对待它。