C++指向虚函数的指针

21

如果您有这样的结构体

struct A {
    void func();
};

还有像这样的引用

A& a;

你可以通过以下方式获取其func方法的指针:

someMethod(&A::func);

那么如果该方法是虚拟的,并且你在运行时不知道它是什么?为什么不能像这样获取指针?

someMethod(&a.func);

是否有可能获得指向该方法的指针?

3个回答

37

成员指针会考虑它们所指向的函数的虚拟性。例如:

#include <iostream>
struct Base
{
    virtual void f() { std::cout << "Base::f()" << std::endl; }
};

struct Derived:Base
{
    virtual void f() { std::cout << "Derived::f()" << std::endl; }
};


void SomeMethod(Base& object, void (Base::*ptr)())
{
    (object.*ptr)();    
}


int main()
{
    Base b;
    Derived d;
    Base* p = &b;
    SomeMethod(*p, &Base::f); //calls Base::f()
    p = &d;
    SomeMethod(*p, &Base::f); //calls Derived::f()    
}

输出:

Base::f()
Derived::f()

1
谢谢!我没有意识到成员函数指针会考虑到这一点。 - Chris
3
@Chris:成员函数指针很智能。这就是为什么它们可以比普通函数指针更大。在多重继承的情况下,它们甚至考虑到基类子对象的偏移量。 - Armen Tsirunyan

12

调用函数指针的方法是同时提供其对象实例指针。这将解决所有虚拟性问题:

struct A { void func(); };

int main()
{
  typedef void (A::*mf)();

  A x; // irrelevant if A is derived or if func is virtual

  mf f = &A::func;   // pointer-to-member-function
  A* p = &x;         // pointer-to-instance

  (p->*f)();           // invoke via pointer
  (x.*f)();            // invoke directly
}

好的,有趣的语法挑战问题:假设我有以下代码。

struct Basil { virtual void foo(); virtual ~Basil(); };
struct Derrek : public Basil { virtual void foo(); };

如果我有 Derrek * p 或者一个 Basil * p,我可以通过 p->Basil::foo() 来调用 Basil 类的成员。如果给我一个 void(Derrek::*q)() = &Derrek::foo,我该如何做呢?

答案:无法实现。仅仅拥有 PMF q 是不够的,它并不知道它指向的是一个虚函数,更何况是哪个虚函数,而且它不能在运行时被用于查找基类的函数。[感谢 Steve 和 Luc!]


我认为你的意思是p->*f()。(但现在假设func是虚函数,可以是A::func或B::func - 你不知道它是哪一个。你如何获取指向正确函数的指针?) - Chris
Chris,我修改了打字错误。在你的原始问题中,你不需要担心,你总是会调用正确的多态函数。 - Kerrek SB
@Chris,在低级别中,对于虚函数,成员函数指针包含一个vtable索引,而不是实际的函数指针。当调用函数指针时,编译器将在vtable中查找索引并调用相应的函数。 - bdonlan
没错。通过 PTMF 调用会调用完全相同的机制,如果 x 是多态的话,那么直接调用 x.func() 会导致在类的 vtable 中查找 func 的最终派生位置。 - Kerrek SB
4
“Syntax challenge”是指,如果你有一个指向未知派生类成员函数的指针,想调用相同名称的基类成员函数,是否可行?我认为不行,尽管我可能错了。使用p->Basil::foo()的关键在于使用全限定名称引用Basil方法 - 只有给定指针,你不知道函数的名称,因此无法在完全限定名称中使用同一符号。 - Steve Jessop
显示剩余3条评论

1

你可以这样获取它的指针:

struct A {
    virtual void foo() = 0;
};

typedef void (A::*LPFOO)();

LPFOO pFoo = &A::foo;

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接