#include<iostream>
using namespace std;
class Class1 {
public:
virtual void f() {
cout << "Function f() in Class1\n";
}
};
class Class2 {
public:
virtual void h() {
cout << "Function h() in Class2\n";
}
};
int main() {
Class1 object1, *p;
Class2 object2;
p = &object1;
p = (Class1*)&object2;
p->f();//possibly abnormal program termination. but in fact, it will call function h(),Why?
return 0;
}
理论上代码必须是错误的,因为p->f()
Class2 中没有 f()功能。
但实际上,代码可以运行,它会在 Class2 中调用Function h(),这很奇怪,为什么?
答案 0 :(得分:3)
由于性能原因,C ++没有很多运行时检查,所以即使它是不合逻辑的,它也会按照你的要求去做。
您告诉它在Class1::f
的内存布局中组织的指针上调用虚方法Class2
,显然Class2::h
与Class1::f
对齐,以便调用它。
这不是标准化的行为,可能在编译器之间有所不同。请参阅Virtual method table。
答案 1 :(得分:2)
代码存在问题,正如@tkausi评论的那样,行为未定义。
问题是你使用的是C风格的强制转换,这很危险,在C ++中应该避免使用。来自cppreference:
遇到C风格的强制转换表达式时,编译器 尝试将其解释为以下强制转换表达式 订单:
const_cast<new_type>(expression);
static_cast<new_type>(expression)
,带扩展名:指针或 此外,还允许对派生类的引用进行转换 指针或对明确基类的引用(反之亦然) 如果基类是不可访问的(也就是说,这个强制转换忽略了 私有继承说明符)。同样适用于将指针指向 成员指向明确的非虚拟基础的成员;static_cast
(带扩展名)后跟const_cast
;reinterpret_cast<new_type>(expression);
- 醇>
reinterpret_cast
后跟const_cast
。满足要求的第一选择 选择相应的演员操作员,即使它不能 编译(见例)。如果演员阵容可以解释多于 一种方式为
static_cast
后跟const_cast
,但不可以 编译。此外,C型铸造符号允许施放, to,以及指向不完整类类型的指针。如果两个表达 并且new_type
是指向不完整类类型的指针,它未指定 是否选择static_cast
或reinterpret_cast
。
因此,在您的情况下,编译器将选择reinterpret_cast
,它只会将对象的原始字节重新解释为其他内容(并且恰好发生在p
点的虚拟表中的第一件事方法h
)。
为确保您的程序安全,您应该使用static_cast
或dynamic_cast
:
#include<iostream>
using namespace std;
class Class1 {
public:
virtual void f() {
cout << "Function f() in Class1\n";
}
};
class Class2 {
public:
virtual void h() {
cout << "Function h() in Class2\n";
}
};
int main() {
Class1 object1, *p;
Class2 object2;
p = &object1;
p = static_cast<Class1*>(&object2); // fails to compile
p->f();
return 0;
}
在这种情况下,编译按预期失败。 实例:https://godbolt.org/g/noErNr
请注意,如果我们将static_cast
行替换为p = dynamic_cast<Class1*>(&object2);
,代码将进行编译,但p
将在运行时设置为nullptr
,并尝试调用{{1}会导致错误。
答案 2 :(得分:1)
理论上代码必须是错误的,因为p-> f()没有函数 Class2中的f()。
代码不仅在理论上是错误的,而且在实践中也是错误的。问题是C ++没有牵着你的手并且没有用棉布包裹你,如果你做错了东西,就会发生坏事。类型安全是你的朋友,但如果你背叛它,你就是靠自己。
在这一行:
p = (Class1*)&object2;
您假装可以将Class2
投射到Class1
。除此之外的任何事情都是未定义的行为,这是一种说法:sc ++并不关心不遵守规则的程序的输出是什么。
与类型系统保持朋友关系的方法是使用static_cast
而不是C风格的演员,这会告诉你不允许演员。