1
class base
{
public:
    virtual void showbase() {
        // ----------
    }
};

class base1 {
public:
    virtual void showbase1() {
        // -------
    }
};

class derived : public base, public base1
{
    void showbase() {
        // ----  
    }

    void showbase1() {
        // -------
    }
};

int main()
{
    base* p = new derived();
    p->showbase1();

    base1* p1 = new derived();
    p1->showbase();
}

根据我对虚函数的理解,编译器处理它的是运行时(vtable 机制),那么为什么我会收到编译时错误。

4

5 回答 5

2

要模拟编译器,请考虑编译器看到的内容:

class base
{
public:
    virtual void showbase() {
        // ----------
    }
};

base* p = /*blah blah*/;
p->showbase1();

是的,base是一个多态类。并且p确实是一个指向base. 但是由于p只指向 a base,重要的是指向 a base1(在哪里showbase1),编译器会像这样解释上面的代码。显然,我是在解释:

Here is a class named `base` with a single virtual method called `showbase`.  
Here is a pointer to a `base` object.  Call the method named `showbase1`

编译器抱怨:

嗯,对不起,伙计,但base没有一个名为 showbase1.


您问:

[我]对虚函数的理解是编译器在运行时处理它。为什么我收到编译时错误?

因为你写的代码是胡说八道。这基本上是多态性的工作原理。

  1. 您使用虚拟方法定义一个基类。
  2. 您定义一个覆盖这些虚拟方法的派生类。
  3. 编译器创建一个 vtable,它将基类中的方法名称映射到派生类中的实现。
  4. 当您通过指向基类的指针(或引用)调用基类中的方法时,将调用派生类的实现。

但是你想要做的是:

  1. 使用虚方法定义基类。
  2. 定义一个覆盖这些虚拟方法的派生类。
  3. 在完全不同的类中调用函数。
于 2013-10-21T20:37:41.517 回答
1

你的基类只知道它们自己的成员函数,所以你不能这样使用它。你可以这样做:

base* p = new derived();
p->showbase();

base1* p1 = new derived();
p1->showbase1();

为了回答您关于运行时多态性的问题,正如您所说,它正在通过 vtable 处理运行时多态性(后期绑定)。但是对于多重继承,每个基类本质上都有一个 vtable。您不能通过指向另一个基类的指针访问一个基类的 vtable。

于 2013-10-21T20:21:58.010 回答
1

指向派生类的基类指针只能访问基类中定义的成员函数。试图通过它访问派生类中定义的其他函数是非法的。在您的情况下base,类没有定义showbase1,因此这是非法的

base* p = new derived();
p->showbase1(); //illegal

但是,您可以这样做:

p->showbase(); // legal because showbase is a member function of base

同样,您无法showbase1使用base类指针访问

base1* p1 = new derived();
p1->showbase();  //illegal
p1->showbase1(); //legal
于 2013-10-21T20:22:11.283 回答
1

根据我对虚函数的理解,编译器处理它的是运行时(vtable 机制),那么为什么我会收到编译时错误。

“处理它”非常模糊,vtables 不是魔术;在 C++ 中,虚拟调度允许调用的实际函数是一个覆盖静态声明的虚拟函数的函数。这意味着在编译时必须知道被覆盖的函数。

vtable 不包含在运行时查找函数所需的信息。相反,它基本上只是一个指向覆盖函数的指针列表。基础提供了其虚函数的完整列表,因此,给定特定的基础类型,编译器在编译时知道该基础的 vtable 中的哪个位置以用于特定的函数覆盖;编译器可以生成直接到 vtable 中的那个位置的代码,获取指针,并调用覆盖函数。

然后,在运行时,当派生类型的实际对象被创建时,派生对象的构造函数会填充基类的 vtable,这样任何检查 vtable 的东西都会得到指向派生类型函数的指针。

因此,您的代码的问题在于,您正在调用的函数showbase()不在编译器知道您正在访问的类型的虚函数列表中base1;编译器无法知道在base1'svtable 中的何处获取名为 的函数覆盖的指针showbase(),因为在 的 vtable 中没有这样的条目base1

于 2013-10-21T20:52:04.177 回答
0

p '静态类型s 类型是类,因此您只能使用它调用已定义为类的函数,即使在最后,将调用派生的函数,因为 p 的动态类型是派生的

p1也会发生同样的事情。

也许你的意思是p->showbase();p1->showbase1();

于 2013-10-21T20:50:23.753 回答