C++虚函数表现奇怪

Dom*_*ino 0 c++ inheritance virtual-functions

我试图理解虚函数和虚继承.在大多数情况下,我认为我成功地掌握了它及其与多态的关系,我一直在阅读vptr如何与派生对象一起工作以及什么不是,但是下面的例子让我失望,这是我在算法和C++中的数据结构书:

#include <iostream>
using namespace std;

class Class1 {
public:
    virtual void f() {
        cout << "Function f() in Class1\n";
    }

    void g() {
        cout << "Function g() in Class1\n";
    }
};

class Class2 {
public:
    virtual void f() {
        cout << "Function f() in Class2\n";
    }

    void g() {
        cout << "Function g() in Class2\n";
    }
};

class Class3 {
public:
    virtual void h() {
        cout << "Function h() in Class3\n";
    }
};

int main() {
    Class1 object1, *p;
    Class2 object2;
    Class3 object3;

    p = &object1;
    p->f();
    p->g();

    p = (Class1*) &object2;
    p->f();
    p->g();

    p = (Class1*) &object3;
    p->f(); // possibly abnormal program termination;
    p->g();
    // p->h(); // h() is not a member of Class1;
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

输出:

Function f() in Class1
Function g() in Class1
Function f() in Class2
Function g() in Class1
Function h() in Class3
Function g() in Class1
Run Code Online (Sandbox Code Playgroud)

除了最后一点,我理解一切p->f();.作为序言,我得到的,我们不能直接调用h()p因铸造成的Class1类型,但不应该Class3的vptr点只对虚函数h()在虚函数表,如果是这样,那么应该不是寻找f()Class3's虚函数表和找不到了?为什么它认为Class1::f()Class3::h(),它不是Class3继承自Class1...而且对于记录,如果我们重写Class3为:

class Class3 : public Class1 { // publicly inherit from Class1 is only difference
    public:
    virtual void h() {
        cout << "Function h() in Class3\n";
    }
};
Run Code Online (Sandbox Code Playgroud)

并向上转换为Class1指针,然后调用p->f()它给我们Class1::f()按预期,我只是无法弄清楚为什么它甚至让我们p->f()Class3不继承时调用Class1.

Mar*_*som 5

这个例子很糟糕,你将彼此不相关的类型.这导致未定义的行为.

代码利用了不同对象类型之间布局中假设的相似性.vtable将位于每个对象的相同位置,虚拟函数将通过索引而不是名称找到.因此,调用第一个虚函数会Class1通过表生成一个调用,从而调用第一个虚函数Class3.请记住,这是一个意外,并不能保证C++的任何属性.


Lig*_*ica 5

我只是想不通为什么它甚至让我们p->f()Class3不继承的时候打电话Class1.

这真的是你要问的.这个问题与虚拟继承或虚函数无关.除上述文本外,您问题中的所有内容都完全无关紧要.

这个问题实际上是为什么你不能阻止在编译时执行以下操作:

struct Foo
{
   void foo() {}
};

struct Bar
{
   void bar() {}
};

int main()
{
   Foo f;
   ((Bar*)&f)->bar();
}
Run Code Online (Sandbox Code Playgroud)

答案是,这是C-风格转换做的:他们让你覆盖类型系统.这取决于你是否正确,而不是欺骗编译器,就像你现在正在做的那样.通过这样做,您的程序具有未定义的行为,就是这样.