假设代码是这样的:
#include <iostream>
using namespace std;
class dog
{
public:
virtual ~dog()
{
}
};
class yellowdog : public dog
{
int age;
public:
void bark() { cout << "woof." << endl;}
};
int main()
{
dog *pd = new dog();
yellowdog *py = dynamic_cast<yellowdog*>(pd);
py->bark();
cout << "py = " << py << endl;
cout << "pd = " << pd << endl;
}
输出:
woof.
py = 0x0
pd = 0x7fd4d34000e0
我理解为什么py = 0.我知道存在运行时检查,并且在运行时检查中,无法将dog
转换为yellowdog
,因此值为py = 0。
我的问题是为什么py->bark()
仍然有效。为什么它仍然能够打印出来&#34; woof。&#34;?
我之前对代码道歉。现在检查..
答案 0 :(得分:7)
py
是一个空指针,因此py->bark()
会调用未定义的行为。任何事情都可能发生,包括似乎有效的事情。与Java或C#不同,如果访问空指针,C ++无法保证立即失败。在效率方面,C ++编译器通常会忽略空指针检查。
碰巧,bark()
根本不使用this
,因此它可以正常工作也就不足为奇了。你不应该依赖它 - 不能保证它会起作用 - 但这并不奇怪。
答案 1 :(得分:1)
铸造失败的两个原因:
您正在转换指向非指针类型的指针,然后将其指定给指针。这应该会给你编译错误。
pd
是指向dog
而不是yellowdog
的指针,因此不能进行向下转换,py
将是空指针(并取消引用此指针)导致undefined behavior)。
来自第二点的未定义行为会给您带来麻烦。根据其定义,未定义的行为是未定义的,任何事情都可能发生。从崩溃到看似工作的事物,再到nasal demons的外观。
答案 2 :(得分:0)
它确实不起作用:从空指针调用方法是未定义的行为。编译器可以决定是否使程序崩溃,如果方法不使用数据成员(在您的情况下会发生这种情况),它仍然可以工作,擦除磁盘中的所有数据,甚至可以在您所在地调用SWAT团队。你很幸运,在你的情况下没有任何不好的事情。
答案 3 :(得分:0)
你在未定义的行为中做了什么,但是你想知道为什么它在你的情况下仍然有效。
原因是函数yellowdog::bark()
没有以任何方式使用this
,加上它被内联到标题中的事实意味着编译器可能只是为你简单地内联它的实现。 / p>
换句话说,它看到你在bark()
指针上调用yellowdog
并知道如何实现它,它用cout << "woof." << endl