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

问题描述 投票:7回答:2

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

Offset
Address/index
virtual?

使用base pointer.调用派生对象时,[偏移量用于指针调整

如何实现此补偿?它是指向某个表的指针吗,每个派生类一个表,并且该表包含(base X, offset)形式的条目?

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

c++ pointers function-pointers multiple-inheritance
2个回答
12
投票

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

换句话说

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

doIt的机器代码与C编译器生成的机器代码相同

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

并且像p->doIt(10, 3.14)这样的调用被编译为P2d$vid$doIt(p, 10, 3.14);

给出一个没有虚拟方法的简单类的方法指针可以作为方法代码的常规指针实现(注意:我将vid用于“ Int + Double的空值” C ++编译器用来处理重载的“名称重整”的玩具示例-名称相同但参数不同的不同函数)。

如果类具有虚方法,则不再正确。

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

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

p->doIt(10, 3.14)p的类似P2d *的调用的代码与C编译器将为其生成的代码相同

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

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

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

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

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

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

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

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


0
投票

这是关于6502答案的评论,但我缺乏声誉。

没有虚拟方法的简单类的方法指针可以实现为该方法的常规指针

我相信此陈述是不正确的,因为多重继承使事情复杂化。考虑以下代码:

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)();
}

一种或另一种方法,指向成员函数的指针需要存储一个偏移量,该偏移量可以在调用时应用于this

© www.soinside.com 2019 - 2024. All rights reserved.