C ++多态不同的虚函数可以用相同的函数名调用吗?

时间:2018-04-14 10:50:42

标签: c++

#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(),这很奇怪,为什么?

3 个答案:

答案 0 :(得分:3)

由于性能原因,C ++没有很多运行时检查,所以即使它是不合逻辑的,它也会按照你的要求去做。

您告诉它在Class1::f的内存布局中组织的指针上调用虚方法Class2,显然Class2::hClass1::f对齐,以便调用它。

这不是标准化的行为,可能在编译器之间有所不同。请参阅Virtual method table

答案 1 :(得分:2)

代码存在问题,正如@tkausi评论的那样,行为未定义。

问题是你使用的是C风格的强制转换,这很危险,在C ++中应该避免使用。来自cppreference

  

遇到C风格的强制转换表达式时,编译器   尝试将其解释为以下强制转换表达式   订单:

     
      
  1. const_cast<new_type>(expression);
  2.   
  3. static_cast<new_type>(expression),带扩展名:指针或   此外,还允许对派生类的引用进行转换   指针或对明确基类的引用(反之亦然)   如果基类是不可访问的(也就是说,这个强制转换忽略了   私有继承说明符)。同样适用于将指针指向   成员指向明确的非虚拟基础的成员;
  4.   
  5. static_cast(带扩展名)后跟const_cast;
  6.   
  7. reinterpret_cast<new_type>(expression);
  8.   
  9. reinterpret_cast后跟const_cast
  10.         

    满足要求的第一选择   选择相应的演员操作员,即使它不能   编译(见例)。如果演员阵容可以解释多于   一种方式为static_cast后跟const_cast,但不可以   编译。此外,C型铸造符号允许施放,   to,以及指向不完整类类型的指针。如果两个表达   并且new_type是指向不完整类类型的指针,它未指定   是否选择static_castreinterpret_cast

因此,在您的情况下,编译器将选择reinterpret_cast,它只会将对象的原始字节重新解释为其他内容(并且恰好发生在p点的虚拟表中的第一件事方法h)。

为确保您的程序安全,您应该使用static_castdynamic_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风格的演员,这会告诉你不允许演员。