查询C++中的运行时多态性

query on runtime polymorphism in C++

本文关键字:运行时 多态性 C++ 查询      更新时间:2023-10-16
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机制)处理它,那么为什么我得到编译时错误。

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

class base
{
public:
    virtual void showbase() {
        // ----------
    }
};
base* p = /*blah blah*/;
p->showbase1();

是的,base是一个多态类。 p确实是一个指向base的指针。 但是由于p只指向一个base,重要的是不指向一个base1showbase1居住的地方),编译器像这样解释上面的代码。 显然,我在解释:

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. 通过指向基类的指针(或 ref)调用基类中的方法时,将调用派生类的实现。

但您要做的是:

  1. 使用虚拟方法定义基类。
  2. 定义重写这些虚拟方法的派生类。
  3. 在完全不同的类中调用函数。

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

"处理它"是相当模糊的,vtables不是魔法;在C++虚拟调度允许调用的实际函数覆盖静态声明的虚拟函数。这意味着在编译时必须知道被覆盖的函数。

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

然后,在运行时,当创建派生类型的实际对象时,派生对象的构造函数填充基的 vtable,以便检查 vtable 的任何内容都将获得指向派生类型函数的指针。

所以你的代码的问题在于你正在调用的函数showbase()不在编译器知道你正在访问的类型的虚函数列表中,base1;编译器无法知道base1's vtable 中的哪个位置获取名为 showbase() 的函数覆盖的指针,因为在 base1 的 vtable 中没有这样的条目。

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

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

但是,您可以这样做:

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

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

base1* p1 = new derived();
p1->showbase();  //illegal
p1->showbase1(); //legal

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

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

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

p'static 类型 s 类型是 base,因此你只能用它调用已定义到 base 中的函数,即使在最后,它也会被调用 Derived 的函数,因为 P 的动态类型派生

同样的事情发生在 p1 上。

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