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

时间:2013-10-21 20:13:19

标签: c++ visual-studio-2008

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机制)一起处理,那么为什么我会收到编译时错误。

5 个答案:

答案 0 :(得分:2)

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

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

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

是的,base是一个多态类。而p确实是指向base的指针。但由于p仅指向base,而且重要的是 指向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. 当您通过指向基类的指针(或引用)调用基类中的方法时,将调用派生类的实现。
  5. 但你要做的是:

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

答案 1 :(得分:1)

您的基类只知道自己的成员函数,因此您不能以这种方式使用它。你可以这样做:

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

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

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

答案 2 :(得分: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

答案 3 :(得分:1)

  

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

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

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

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

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

答案 4 :(得分:0)

p '静态类型的类型是 base ,因此您只能调用已定义为 base的函数即使最后,也会调用来自派生的函数,因为p的动态类型是派生的

p1也是如此。

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