[英]How is pointer to member function implemented in C++?
c++中指向成员函数的指针分为三部分:
Offset
Address/index
virtual?
偏移量用于在使用base pointer.
这个偏移量是如何实现的? 它是指向某个表的指针,每个派生类都有一个表并且该表包含形式为(base X, offset)
的条目吗?
另外,我在哪里可以获得有关此的更多信息?
首先,您应该注意到 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
用于“Void of Int+Double”作为“name mangling”的玩具示例“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
而不是派生的,因为在这种情况下调用可以是相同的至于非虚拟方法)。
方法指针需要遵守虚拟方法...即,在从P2d
派生的实例上使用方法指针间接调用doIt
需要调用派生版本,而在P2d
实例上使用时,需要使用相同的方法指针来调用基础版本。 这意味着选择调用哪个代码取决于指针和类实例。
一种可能的实现方式是使用蹦床:
void MethodPointerCallerForP2dDoit(P2d *p, int a, double b) {
p->doIt(a, b);
}
在这种情况下,方法指针仍然只是指向代码的指针(但指向蹦床,而不是最终方法)。
另一种方法是将方法的索引存储为方法指针,而不是在 VMT 中。 这是可行的,因为在 C++ 中,方法指针绑定到特定的类,因此编译器知道该类是否有虚方法。
多重继承不会使方法指针复杂化,因为所有内容都可以在编译时解析为单个最终 VMT 表。
这是关于 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
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.