多态和成员函数指针如何工作?

时间:2013-07-28 16:54:40

标签: c++ polymorphism

我有以下代码:

#include <iostream>
using namespace std;

class Base 
{
public:
    virtual void WhoAmI() const;
    typedef void (Base::*WhoPtr)() const;
};

class Derived : public Base 
{
public:
    virtual void WhoAmI() const;
};

void Derived::WhoAmI() const 
{
    cout << "I am the derived" << endl;
}

void Base::WhoAmI() const 
{
    cout << "I am the base" << endl;
}

int main() 
{
    Base::WhoPtr func = &Base::WhoAmI;
    Base theBase;
    (theBase.*func)();
    Derived theDerived;
    (theDerived.*func)();
    cin.get();
    return 0;
}   

让我们专注于主要:

int main() 
{
    Base::WhoPtr func = &Base::WhoAmI;
    Base theBase;
    (theBase.*func)();
    Derived theDerived;
    (theDerived.*func)();
    cin.get();
    return 0;
}   

我们有一个本地变量func,其中包含Base::WhoAmI的地址。

此外,我们还有BaseDerived个对象。

在第2行,我们从基地调用指向func(theBase.*func)()

直到现在我才明白。

之后的2行,我们从派生的(theDerived.*func)()中调用它。

打印:I am the derived。为什么呢?

这两个WhoAmI都是virtual,这意味着呼叫依赖于pointed object,而不是类型。

指向的对象是func,属于Base。为什么打印I am the derived而不是I am the base

3 个答案:

答案 0 :(得分:1)

你为什么感到惊讶你有一个指向成员函数的指针 指向虚拟功能。如果你拿了地址 theDerived或对其的引用,并初始化Base*或 一个Base&,你希望ptrToBase->WhoAmI()来调用它 派生类中的函数。毕竟,这就是你使用的原因 一个虚拟的功能开始。当你这么做时 通过指向成员函数的指针调用。表达方式 &Base::WhoAmI产生一个指向(虚拟)成员函数的指针。

答案 1 :(得分:0)

指向的对象是theDerived。您选择的方法是Base::whoAmI,请注意方法名称包含类引用(静态)但不包含对象引用(动态)。要调用的虚函数由对象的运行类型时间决定,该对象用作方法的this

答案 2 :(得分:0)

关于虚函数的重点是,它是一个运行时决定,根据相关对象的动态类型调用哪个版本。这与非虚函数调用非常不同,其中编译器本身根据对象的声明的类型做出决策,而不管实际运行时对象的类型是什么。

为了实现这一点,每个类都有一个虚函数表(一个vtable),它的所有实例都有一个运行时的隐式指针。现在,当您创建Base实例时,实例的vtable指针将指向Base的vtable。同样,Derived的实例将指向Derived的vtable。

在这两个vtable中,您的示例中WhoAmI()只有一个条目,Base的vtable中的指针指向Base::WhoAmI(),而Derived的vtable点中的指针指向Derived::WhoAmI()WhoAmI()

因此,当您调用WhoAmI()时,运行时将从对象中查找vtable,然后查找将要执行的函数的函数指针。

这就是说,从你看到的行为中可以明显看出,成员函数指针是什么:没有多于或少于vtable的偏移量!在您的情况下,此偏移很可能只是零,因为{{1}}是vtable中的第一个也是唯一的条目。当你调用成员函数指针后面的函数时,你给它一个查找vtable的对象。然后,vtable的偏移量(成员函数指针)用于加载指向执行的实际代码的指针,就像对虚函数的任何其他调用一样。

唯一的区别是,在正常的虚函数调用中,当您使用成员函数指针时,编译器将知道通过您调用的函数的名称查找函数指针的精确偏移量,该偏移量由成员函数指针在运行时提供。