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

发布于 2024-07-26 00:54:40 字数 462 浏览 4 评论 0原文

考虑下面的 C++ 代码:

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


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

如果我必须猜测,我会说 &A::f 在这种情况下意味着“A 的 f() 实现的地址”,因为指针之间没有显式分隔常规成员函数和虚拟成员函数。 由于 A 没有实现 f(),这将是一个编译错误。 然而,事实并非如此。

不仅如此。 下面的代码:

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

实际上会调用B::f。

这是怎么发生的?

Consider the following C++ code:

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. And since A doesn't implement f(), that would be a compile error. 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.

How does it happen?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

零度℉ 2024-08-02 00:54:40

它之所以有效,是因为标准规定它应该如何发生。 我用 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;
}

该程序输出 1 5 - 这两个函数的虚拟表条目的字节偏移量。 它遵循Itanium C++ ABI指定

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.

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. It follows the Itanium C++ ABI, which specifies that.

独木成林 2024-08-02 00:54:40

这里有太多关于成员函数指针的信息。 在“The Well-Behaved Compilers”下有一些关于虚拟函数的内容,尽管 IIRC 当我阅读这篇文章时我只是略读了那部分,因为这篇文章实际上是关于在 C++ 中实现委托的。

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

简而言之,它取决于编译器,但一种可能性是成员函数指针被实现为包含指向进行虚拟调用的“thunk”函数的指针的结构。

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++.

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.

哽咽笑 2024-08-02 00:54:40

我不完全确定,但我认为这只是常规的多态行为。 我认为 &A::f 实际上意味着类的 vtable 中函数指针的地址,这就是为什么您没有收到编译器错误的原因。 vtable 中的空间仍然被分配,这就是您实际要返回的位置。

这是有道理的,因为派生类本质上用指向其函数的指针覆盖这些值。 这就是 (a->*f)() 在第二个示例中起作用的原因 - f 引用在派生类中实现的 vtable。

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. The space in the vtable is still allocated, and that is the location you are actually getting back.

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文