多态性和成员函数指针是如何工作的
How does polymorphism and member function pointers works?
我有以下代码:
#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
的地址。
此外,我们还有Base
和Derived
对象。
在第2行中,我们从底部调用有点的func
:(theBase.*func)()
。
直到现在我才明白。
2行之后,我们从派生的中调用此:(theDerived.*func)()
。
它打印:I am the derived
。为什么?
两个WhoAmI
都是virtual
,这意味着调用依赖于pointed object
,而不是类型。
所指向的对象是属于CCD_ 13的CCD_。为什么打印I am the derived
而不是I am the base
?
你为什么感到惊讶。您有一个指向成员函数的指针指向虚拟函数。如果你取了theDerived
或对其的引用,并初始化Base*
或有了Base&
,您会期望ptrToBase->WhoAmI()
调用派生类中的函数。毕竟,这就是你使用一开始是一个虚拟函数。当你通过指向成员函数的指针调用。表达式CCD_ 20产生一个指向(虚拟)成员函数的指针。
指向的对象是theDerived
。您选择的方法是Base::whoAmI
,请注意,方法名称包含类引用(静态),但不包含对象引用(动态)。调用什么虚拟函数取决于用作该方法的this
的对象的运行类型时间。
虚拟函数的全部意义在于,它是运行时根据所讨论对象的动态类型来决定调用哪个版本。这与非虚拟函数调用非常不同,在非虚拟函数中,编译器本身根据对象的声明的类型做出决定,而不管实际的运行时对象是什么类型。
为了实现这一点,每个类都有一个虚拟函数表(vtable),它的所有实例都在运行时有一个隐式指针指向该表。现在,当您创建Base的实例时,该实例的vtable指针将指向Base的vtable。同样,Derived的实例将有一个指向Derived vtable的指针。
在这两个vtable中,在您的示例中,WhoAmI()
只有一个条目,Base的vtable中的指针指向Base::WhoAmI()
,Derived的vtable指向Derived::WhoAmI()
。
因此,当您调用WhoAmI()
时,运行时将从对象中查找vtable,然后查找指向将要执行的函数的函数指针。
也就是说,从您所看到的行为中可以明显看出,成员函数指针是什么:只不过是vtable的偏移量!在您的情况下,这个偏移很可能只是零,因为WhoAmI()
是vtable中的第一个也是唯一一个条目。当您调用成员函数指针后面的函数时,您会给它一个对象,从中可以查找vtable。然后,vtable(成员函数指针)中的偏移量用于加载指向实际执行代码的指针,就像在对虚拟函数的任何其他调用中一样。
唯一的区别是,在正常的虚拟函数调用中,编译器将通过调用的函数的名称来知道查找函数指针的精确偏移量,当您使用成员函数指针时,该偏移量在运行时由成员函数指针提供。
- QSqlquery prepare()和bindvalue()不工作
- 导入库可以跨dll版本工作吗
- 以螺旋方式打印矩阵的程序.(工作不好)
- 对象指针在c++中是如何工作的
- 为什么在Windows上的VS 2019和Clang 9中"size_t"在没有标题的情况下工作
- VSOMEIP-2个设备之间的通信(TCP/UDP)不工作
- 为字符串中每 N 个字符插入空格的函数没有按照我认为的方式工作?
- C++为线程工作动态地分割例程
- 为什么我的 std::ref 无法按预期工作?
- 布尔比较运算符是如何在C++中工作的
- SampleConsensusPrerejective(ext.RANSAC)是如何真正工作的
- 不确定要在我的main中放入什么才能使我的代码正常工作
- 为什么std::condition_variable notify_all的工作速度比notify_one快(对于随机请
- <<操作员在下面的行中工作
- 有人能解释一下为什么下界是这样工作的吗C++的
- ExtractIconEx:可以工作,但偶尔会崩溃
- C++中的memset函数工作不正常
- 当我在第一个循环中使用"auto"时,它工作正常,但是使用"int"它会给出错误,为什么?
- 当 int 方法工作正常时,void 方法有何不同,或者为什么我不能调用 void 方法?
- sdl软件渲染器不工作,工作在硬件加速的一个