简体   繁体   English

C++中如何实现指向成员函数的指针?

[英]How is pointer to member function implemented in C++?

The pointer to member function in c++ is in three parts: c++中指向成员函数的指针分为三部分:

Offset
Address/index
virtual?

Offset is used for Pointer adjustment when derived object is called using base pointer.偏移量用于在使用base pointer.

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) ?它是指向某个表的指针,每个派生类都有一个表并且该表包含形式为(base X, offset)的条目吗?

Also, where can I get more info about 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 .首先,您应该注意到 C++ 方法可以像常规函数一样实现(并且通常实现),它在所有其他参数之前接受一个额外的隐藏参数,名为this

In other words in换句话说在

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

the machine code for doIt is the same that would be generated by a C compiler for doIt的机器代码与 C 编译器为

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

and a call like p->doIt(10, 3.14) is compiled to P2d$vid$doIt(p, 10, 3.14);p->doIt(10, 3.14)这样的调用被编译为P2d$vid$doIt(p, 10, 3.14);

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).鉴于此,一个没有虚方法的简单类的方法指针可以作为指向方法代码的常规指针来实现(注意:我将vid用于“Void of Int+Double”作为“name mangling”的玩具示例“C++ 编译器处理重载的方式——具有相同名称但参数不同的不同函数)。

If the class has virtual methods however this is no more true.但是,如果该类具有虚方法,则情况不再如此。

Most C++ compilers implement virtual dispatching unsing a VMT... ie in大多数 C++ 编译器在不使用 VMT 的情况下实现虚拟调度...即

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

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->doIt(10, 3.14)的代码,其中pP2d *与 C 编译器生成的代码相同

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

ie 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).即实例包含一个指向虚拟方法表的隐藏指针,每个成员都包含有效代码地址(假设编译器无法推断p的类确实是P2d而不是派生的,因为在这种情况下调用可以是相同的至于非虚拟方法)。

Method pointers are required to respect virtual methods... ie 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.方法指针需要遵守虚拟方法...即,在从P2d派生的实例上使用方法指针间接调用doIt需要调用派生版本,而在P2d实例上使用时,需要使用相同的方法指针来调用基础版本。 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).在这种情况下,方法指针仍然只是指向代码的指针(但指向蹦床,而不是最终方法)。

An alternative would be to store as method pointer the index of the method inside the VMT instead.另一种方法是将方法的索引存储为方法指针,而不是在 VMT 中。 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.这是可行的,因为在 C++ 中,方法指针绑定到特定的类,因此编译器知道该类是否有虚方法。

Multiple inheritance do not complicate things for method pointers because everything can be just resolved to a single final VMT table at compile time.多重继承不会使方法指针复杂化,因为所有内容都可以在编译时解析为单个最终 VMT 表。

This is a comment regarding 6502's answer, but I lack reputation.这是关于 6502 的回答的评论,但我缺乏声誉。

a method pointer for a simple class that has no virtual methods can be implemented as a regular pointer to the method没有虚方法的简单类的方法指针可以实现为指向该方法的常规指针

I believe this statement is incorrect because multiple inheritance complicates things.我认为这种说法是不正确的,因为多重继承会使事情复杂化。 Consider this code:考虑这段代码:

struct A {
    int a;
    void f() {
        // use a
    }
};

struct B {
    int b;
    void g() {
        // use b
    }
};

// C objects may look like this in memory:
//
//     |-----|
//     |  A  |
//     |-----|
//     |  B  |
//     |-----|
//
// Since only one of A and B can be at the start of C in terms of memory
// layout, at least one of the following can't work:
//
// * naively interpreting a pointer to C as a pointer to A
// * naively interpreting a pointer to C as a pointer to B
struct C : A, B {};

void call_ptm(void (C::*ptm)()) {
    C c;
    // `ptm` could be `A::f` or `B::g`.  We don't know.  At what offset
    // relative to `this` do we expect to find data members then?  It depends
    // on whether `ptm` points to `A::f` or `B::g`, which isn't known at
    // compile time.
    (c.*ptm)();
}

One way or another, a pointer to a member function needs to store an offset that can be applied to this upon invocation.不管怎样,指向成员函数的指针需要存储一个偏移量,该偏移量可以在调用时应用于this

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM