虚函数——指针在哪里?
首先举一个例子。
class A
{
public:
virtual const char* GetName() { return "A"; }
};
class B: public A
{
public:
virtual const char* GetName() { return "B"; }
};
class C: public B
{
public:
virtual const char* GetName() { return "C"; }
};
class D: public C
{
public:
virtual const char* GetName() { return "D"; }
};
int main()
{
C cClass;
A &rBase = cClass;
cout << "rBase is a " << rBase.GetName() << endl;
return 0;
}
在这个特定示例中,输出为:
rBase is a C
以下是其工作原理的步骤:
rBase 是 A 类型的指针,因此它转到 A 类并查找 GetName()。但是 GetName() 在那里是虚拟的,因此编译器检查 A 和 C 之间的所有类,并从最派生的类(即 C)中获取 GetNAme() 函数
但是,我的疑问是编译器如何知道哪个是类 A 的子级它如何能够从父类转移到B类等等?孩子知道它是父母,但父母不知道它是孩子(我认为!)。
从我的角度来看,正确的执行步骤应该是:
rBase 是 A 类型的指针,因此它会转到A 类并查找 GetName()。但 GetName() 是虚拟的,因此编译器会检查指针指向哪个类。在这种情况下,C 类对象,因此转到 C 类并检查它是否具有函数 GetName(),以便使用它。如果该函数不存在于 C 类中(假设),编译器可以轻松跟踪 C 的父级并检查相同的情况,这可以继续下去,直到返回到 A(假设除 A 之外的所有类都不包含 GetName())。
现在,这似乎是一种更合乎逻辑的方法,因为在继承树中向后移动(从子到父)似乎比向前移动(从父到子)更容易实现。
问候,
An example to start with.
class A
{
public:
virtual const char* GetName() { return "A"; }
};
class B: public A
{
public:
virtual const char* GetName() { return "B"; }
};
class C: public B
{
public:
virtual const char* GetName() { return "C"; }
};
class D: public C
{
public:
virtual const char* GetName() { return "D"; }
};
int main()
{
C cClass;
A &rBase = cClass;
cout << "rBase is a " << rBase.GetName() << endl;
return 0;
}
In this particular example the output is :
rBase is a C
Here are the steps of how it works:
rBase is a pointer of type A, so it goes to class A and looks for GetName(). But GetName() is virtual there so compiler checks all classes between A and C and takes GetNAme() function from the most derived class ie C
But, my doubt is that how will the compiler know which is the child of class A and how will it be able to move to class B and so on from Parent class to child class ? A child know it's parent but the parent doesn't know it's children (I think!).
From my point of view the correct steps of execution should have been:
rBase is a pointer of type A, so it goes to class A and looks for GetName(). But GetName() is virtual there so the compiler checks what class the pointer is pointing to. C class object in this case, so goes to C class and checks if it has function GetName() so uses it. If the function was not present in C class (assume) the compiler can easily trace C's parent and check for the same and this could go on until it reaches back to A (assuming all classes except A doesn't contain GetName()).
Now, this seems a more logical method because moving backward (child to parent) in an inheritance tree seems more achievable than moving forward (parent to child).
Regards,
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
您描述的第二个算法是“正确的”,因为它可以有效地以这种方式解决问题。然而,编译器使用了一个很好的技巧来快进该算法。基本上,它使用通常称为虚拟方法表的查找,通常缩写为“vtable”。
基本上,具有虚方法的类的实例保存指向其类的虚方法表的指针。编译器将虚拟方法名称映射到虚拟表中的偏移量,以便调用虚拟表不需要复杂的算法:所需的只是数组查找,然后在结果地址处进行调用。
The 2nd algorithm you describe is "correct" in that it would effectively resolve things this way. However, the compiler uses a nice trick to fast-forward that algorithm. Basically, it uses a lookup commonly called a virtual method table, often abbreviated to "vtable".
Basically, instances of classes with virtual methods hold a pointer to their class' virtual method table. The compiler maps virtual method names to offsets in the virtual table, such that invoking a virtual table does not need a complex algorithm: all that's necessary is an array lookup followed by a call at the resulting address.
不,这不是它的工作原理。编译器在生成通过指向
A
的指针调用virtual GetName()
的代码时,只会输入类似“调用与该对象关联的虚拟表中的第 7 个条目”的内容。当生成重写GetName()
的派生类时,编译器会将其GetName()
实现放在虚拟表的此项中。这样父类就不需要知道它的子类了。孩子们有责任正确填写他们的虚拟桌子。
No, this is not how it works. The compiler when generating the code to call
virtual GetName()
through pointer toA
just puts something like "call the 7th entry in the virtual table associated with the object". When generating a derived class that overridesGetName()
, the compiler will put itsGetName()
implementation at this entry in the virtual table.This way the parent class does not need to know its children. It's the children responsibility to fill their virtual table correctly.