简体   繁体   English

指向虚成员函数的指针。 它是如何工作的?

[英]Pointers to virtual member functions. How does it work?

Consider the following C++ code:考虑以下 C++ 代码:

class A
{
public:
      virtual void f()=0;
};


int main()
{
     void (A::*f)()=&A::f;
}

If I'd have to guess, I'd say that &A::f in this context would mean "the address of A's implementation of f()", since there is no explicit seperation between pointers to regular member functions and virtual member functions.如果我不得不猜测,我会说 &A::f 在这个上下文中的意思是“A 的 f() 实现的地址”,因为在指向常规成员函数和虚拟成员函数的指针之间没有明确的分隔. And since A doesn't implement f(), that would be a compile error.由于 A 没有实现 f(),这将是一个编译错误。 However, it isn't.然而,事实并非如此。

And not only that.而且不仅如此。 The following code:以下代码:

void (A::*f)()=&A::f;
A *a=new B;            // B is a subclass of A, which implements f()
(a->*f)();

will actually call B::f.实际上会调用 B::f。

How does it happen?它是如何发生的?

It works because the Standard says that's how it should happen.它之所以有效,是因为标准说它应该是这样发生的。 I did some tests with GCC, and it turns out for virtual functions, GCC stores the virtual table offset of the function in question, in bytes.我用 GCC 做了一些测试,结果是对于虚拟函数,GCC 以字节为单位存储相关函数的虚拟表偏移量。

struct A { virtual void f() { } virtual void g() { } }; 
int main() { 
  union insp { 
    void (A::*pf)();
    ptrdiff_t pd[2]; 
  }; 
  insp p[] = { { &A::f }, { &A::g } }; 
  std::cout << p[0].pd[0] << " "
            << p[1].pd[0] << std::endl;
}

That program outputs 1 5 - the byte offsets of the virtual table entries of those two functions.该程序输出1 5 - 这两个函数的虚拟表条目的字节偏移量。 It follows the Itanium C++ ABI , which specifies that .它遵循Itanium C++ ABI它指定.

Here is way too much information about member function pointers.这里有太多关于成员函数指针的信息。 There's some stuff about virtual functions under "The Well-Behaved Compilers", although IIRC when I read the article I was skimming that part, since the article is actually about implementing delegates in C++.在“The Well-Behaved Compilers”下有一些关于虚函数的内容,尽管 IIRC 在我阅读这篇文章时我正在略读那部分,因为这篇文章实际上是关于在 C++ 中实现委托的。

http://www.codeproject.com/KB/cpp/FastDelegate.aspx http://www.codeproject.com/KB/cpp/FastDelegate.aspx

The short answer is that it depends on the compiler, but one possibility is that the member function pointer is implemented as a struct containing a pointer to a "thunk" function which makes the virtual call.简短的回答是它取决于编译器,但一种可能性是成员函数指针被实现为一个结构,其中包含一个指向“thunk”函数的指针,该函数进行虚拟调用。

I'm not entirely certain, but I think it's just regular polymorphic behavior.我不完全确定,但我认为这只是常规的多态行为。 I think that &A::f actually means the address of the function pointer in the class's vtable, and that's why you aren't getting a compiler error.我认为&A::f实际上表示类的 vtable 中函数指针的地址,这就是为什么您没有收到编译器错误的原因。 The space in the vtable is still allocated, and that is the location you are actually getting back. vtable 中的空间仍然分配,这就是您实际返回的位置。

This makes sense because derived classes essentially overwrite these values with pointers to their functions.这是有道理的,因为派生类本质上用指向它们的函数的指针覆盖了这些值。 This is why (a->*f)() works in your second example - f is referencing the vtable that is implemented in the derived class.这就是(a->*f)()在您的第二个示例中起作用的原因 - f引用了在派生类中实现的 vtable。

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

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