如何在C ++中实现指向成员函数的指针? [英] How is pointer to member function implemented in C++?

查看:129
本文介绍了如何在C ++中实现指向成员函数的指针?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在c ++中,指向成员函数的指针分为三个部分:

The pointer to member function in c++ is in three parts:

Offset
Address/index
virtual?

使用base pointer.

该偏移量如何实现?它是指向某个表的指针吗,每个派生类的一个表,并且该表包含格式为(base X, offset)的条目?

How is this offset implemented? Is it pointer to some table, one table for each derived class and the table contains entries of the form (base X, offset)?

此外,我在哪里可以获得有关此信息的更多信息?

Also, where can I get more info about this?

推荐答案

首先,您应该注意,C ++方法可以被实现(并且通常是被实现的),就像常规函数可以在所有其他参数之前接受额外的隐藏参数一样,名为this.

First you should note that a C++ method can be implemented (and is normally implemented) just as a regular function that accepts an extra hidden parameter before all other parameters, named this.

换句话说

struct P2d {
   double x, y;
   void doIt(int a, double b) {
       ...
   }
};

doIt的机器代码与C编译器为

生成的机器代码相同

the machine code for doIt is the same that would be generated by a C compiler for

void P2d$vid$doIt(P2d *this, int a, double b) {
    ...
}

和类似p->doIt(10, 3.14)的调用被编译为P2d$vid$doIt(p, 10, 3.14);

and a call like p->doIt(10, 3.14) is compiled to P2d$vid$doIt(p, 10, 3.14);

为此,针对没有虚拟方法的简单类的方法指针可以实现为方法代码的常规指针(注意:我将vid用作"Int Void of Int + Double"作为C ++编译器用于处理重载的名称修改"的玩具示例-具有相同名称但参数不同的不同函数).

Given this a method pointer for a simple class that has no virtual methods can be implemented as a regular pointer to the method code (NOTE: I'm using vid for "Void of Int+Double" as a toy example of the "name mangling" that C++ compilers do to handle overloads - different functions with the same name but different parameters).

如果该类具有虚方法,则不再适用.

If the class has virtual methods however this is no more true.

大多数C ++编译器在未分配VMT的情况下实现虚拟分派...即

Most C++ compilers implement virtual dispatching unsing a VMT... i.e. in

struct P2d {
    ...
    virtual void doIt(int a, double b);
};

p->doIt(10, 3.14)这样的调用的代码,其中pP2d *,与C编译器生成的代码相同

the code for a call like p->doIt(10, 3.14) where p is a P2d * is the same that a C compiler would generate for

(p->$VMTab.vid$doIt)(p, 10, 3.14);

即该实例包含一个指向虚拟方法表的隐藏指针,该虚拟方法表的每个成员都包含有效代码地址(假设编译器无法推断p的类确实是P2d而不是派生类,在这种情况下,调用可以与非虚拟方法相同.

i.e. the instance contains a hidden pointer to a virtual method table that for each member contains the effective code address (assuming the compiler cannot infer that the class of p is indeed P2d and not a derived, as in that case the call can be the same as for a non-virtual method).

方法指针来尊重虚拟方法...即,需要使用从P2d派生的实例上的方法指针间接调用doIt来调用派生版本,而使用相同的方法指针来调用基类在P2d实例上使用时的版本.这意味着选择要调用的代码取决于指针和类实例.

Method pointers are required to respect virtual methods... i.e. calling doIt indirectly using a method pointer on an instance derived from P2d is required to call the derived version while the same method pointer is instead to call the base version when used on P2d instances. This means the selection of which code to call depends on both the pointer and the class instance.

可能的实现方式是使用蹦床:

A possible implementation is using a trampoline:

void MethodPointerCallerForP2dDoit(P2d *p, int a, double b) {
    p->doIt(a, b);
}

并且在这种情况下,方法指针仍然只是指向代码的指针(但指向蹦床,而不是最终方法).

and in this case a method pointer is still just pointer to code (but to the trampoline, not to the final method).

一种替代方法是将方法的索引存储在VMT内作为方法指针.这是可行的,因为在C ++中,方法指针绑定到特定的类,因此编译器知道该类是否存在虚拟方法.

An alternative would be to store as method pointer the index of the method inside the VMT instead. This would be feasible because in C++ the method pointer is tied to a specific class and therefore the compiler knows if for that class there are virtual methods or not.

多重继承不会使方法指针复杂化,因为在编译时所有内容都可以解析为一个最终的VMT表.

Multiple inheritance do not complicate things for method pointers because everything can be just resolved to a single final VMT table at compile time.

这篇关于如何在C ++中实现指向成员函数的指针?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆