C ++虚函数表现奇怪

时间:2015-05-27 16:28:35

标签: 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;
}

输出:

Function f() in Class1
Function g() in Class1
Function f() in Class2
Function g() in Class1
Function h() in Class3
Function g() in Class1

我理解除了上一个p->f();之外的所有内容。作为序言,我知道我们无法直接从h()调用p,因为要转换为Class1类型,但Class3 vptr不应仅指向虚拟函数h()在其vtable中,如果是,那么它不应该在f() vtable中查找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";
    }
};

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

3 个答案:

答案 0 :(得分:5)

此示例错误,您正在为彼此投射不相关的类型。这会导致未定义的行为。

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

答案 1 :(得分:5)

  

我无法理解为什么当p->f()未从Class3继承时,它甚至会让我们致电Class1

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

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

struct Foo
{
   void foo() {}
};

struct Bar
{
   void bar() {}
};

int main()
{
   Foo f;
   ((Bar*)&f)->bar();
}

答案是这就是C风格演员:它们允许你覆盖类型系统。正如你现在所做的那样,由你来做正确而不是欺骗编译器。通过这样做,您的程序具有未定义的行为以及那个。

答案 2 :(得分:2)

您或该书的作者可能只是忘记了父/子继承关系,以使此示例代码比未定义的行为更有价值:)

#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 Class1 {
public:
  virtual void f() {
    cout << "Function f() in Class2\n";
  }

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

class Class3 : public Class1 {
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();
  p->g();
  // p->h(); // h() is not a member of Class1;
  return 0;
}

打印哪些:

Function f() in Class1
Function g() in Class1
Function f() in Class2
Function g() in Class1
Function f() in Class1
Function g() in Class1

这是一个不错的基本多态性示例。