使用函数指针调用虚成员函数的基类定义

22

我希望使用成员函数指针调用虚函数的基类实现。

class Base {
public:
    virtual void func() { cout << "base" << endl; }
};

class Derived: public Base {
public:
    void func() { cout << "derived" << endl; }

    void callFunc()
    {
        void (Base::*fp)() = &Base::func;
        (this->*fp)(); // Derived::func will be called.
                       // In my application I store the pointer for later use,  
                       // so I can't simply do Base::func().
    }
};

在上述代码中,从callFunc调用派生类实现的func。我是否可以保存指向Base :: func的成员函数指针,还是必须以某种方式使用using?在我的实际应用中,我使用boost :: bind在callFunc中创建boost :: function对象,稍后我将使用它来从程序的另一部分调用func。因此,如果boost :: bind或boost :: function有某种方法可以解决这个问题,那也会有所帮助。

可能是重复问题:C++:指向虚拟成员函数单态版本的指针? - Eliah Kagan
6个回答

13
当你通过引用或指针调用虚方法时,你总是会激活虚调用机制来找到最派生类型。
你最好添加一个非虚函数作为备选方案。

2
很抱歉,您想要做的事情是不可能的。成员函数指针被设计用来维护所指向函数的虚拟性。

1
你的问题在于成员函数指针并不完全等同于裸函数指针。它实际上不仅仅是一个指针,而是一个相当复杂的结构,其细节因编译器实现而异。当你通过语法(this->*fp)()调用它时,你实际上是在原始对象上调用它,这会导致虚函数分派。
可能有效的一种方法是将其转换为非方法指针类型。这有点脆弱,但我认为它应该可以工作。你仍然需要传递一个Base *,但你需要显式地进行,并且虚函数分派被绕过了:
typedef void BasePointer(Base*);

void callFunc()
{
    BasePointer fp = (BasePointer *)&Base::func;
    fp(this);
}

更新:好的,不行,你不能那样做。这是非法的,即使合法也不安全。C++ FAQ更多相关信息。但知道这些并不能解决你的问题。问题在于,如果你想通过一个Base指针调用Base::func,指针必须指向一个Base对象。无论是指向对象的指针还是成员指针。如果你能安排好这点,那么你就可以使用成员函数指针了。

这里有另一个想法,不太美观,但至少可行。在Derived中提供一个非虚拟的函数,明确调用Base::func。指向那个函数。如果你需要在许多不同变量的funccallFunc中实现这一点,那么它不会很好扩展,但对于一个方法来说,它可以正常工作。


1
那肯定行不通!将 sizeof(&Base::func)sizeof(BasePointer) 进行比较 :o - Marc Mutz - mmutz
mmutz:是的。我尝试在发布代码之前进行测试,但今天我没有留出时间来做。可能一个 static_cast 可以足够聪明地从成员函数指针中弹出函数指针部分,但我担心我的Python函数概念现在正在覆盖我的C++ : )。 - quark
这是错误的:“它不仅携带要调用的函数的地址,还携带要在其上调用该函数的对象”。 - Éric Malenfant
Eric:你能给我指一些相关文档吗?我的理解是成员函数指针特指将函数指针与成员指针配对。这就是为什么在我尝试的每个编译器上,sizeof(&Base::func)都是普通指针的两倍。 - quark
@quark:"&Base::func" 中的“对象”是什么?“Base”是一种类型,而不是实例。还要注意,在使用指针时必须提供实例。(例如:BasePointer fp = &Base::func; Base b; b.*fp();)。 - Éric Malenfant
显示剩余3条评论

0

为什么要通过函数指针来实现这个?有什么特殊的原因吗?

你应该可以直接写成:

Base::func();

调用基类实现。


1
正如我在问题中所写的那样,我将指针保存在callFunc中,但是在程序的另一个地方使用它来实际调用func。 - Eddie
对于虚函数,这种方法行不通。它总是会进行 vtable 查找并在派生类上调用该方法。 - Joel

0

这里有什么问题吗?

(Base(*this).*fp)();

如果您对此感到满意,那么问题就是为什么您首先要使用函数指针。我认为更多的上下文可能会有所帮助。


这已经被quark建议过了(“...如果你想通过Base指针调用Base :: func,那么它所指向的对象也必须是Base。”)。我假设Eddie不能保证在调用函数时知道要切片的类型。 - Troubadour

0
除了quark所说的之外,更一般的建议是你应该使用信号/槽实现而不是裸函数指针。Boost有一个,还有libsigc和一堆其他的库。

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