使用基类函数指针访问派生类成员函数

3

我在我的项目中使用函数指针,但遇到了问题,创建了一个测试用例来展示它...以下代码在MSVC2005上失败并显示下面的错误(简单来说,我想通过基类函数指针访问派生类函数)

错误 C2440:'=':无法从“void(__thiscall ClassB::*)(void)”转换为“ClassAFoo”

class ClassA {
 public: 
    virtual void foo() 
    {
       printf("Foo Parent"); 
    }
};

typedef void (ClassA::*ClassAFoo)();

class ClassB : public ClassA {
 public:
    virtual void foo() 
    { 
        printf("Foo Derived"); 
    }
};

int main() {
    ClassAFoo fPtr;
    fPtr = &ClassB::foo;
}

我的问题如下:

  1. 如果我通过基类函数指针访问派生类函数失败,这是C++行为还是编译器错误?
  2. 对于上述情况,如果我注释掉ClassB::foo,这段代码就能顺利编译,没有任何进一步的修改。为什么会这样?fPtr = &ClassB::foo;不应该再次导致编译时错误吗?

可能是重复的问题:为什么我无法将指向派生类成员函数的指针转换为相同但属于基类的指针?(原文链接:https://dev59.com/dWLVa4cB1Zd3GeqPy7ez) - John Zwinck
@JohnZwinck,你能否请稍微解释一下吗? - Saqlain
3个回答

5
  1. 这是正确的行为。可以这样想:所有的ClassB实例都有成员ClassA::foo,但并不是所有的ClassA实例都有成员ClassB::foo;只有那些作为ClassB实例的基类子对象的ClassA实例才具有该成员。因此,将ClassB::foo赋值给ClassAFoo,然后将ClassAFoo与“纯粹”的ClassA对象组合使用,会尝试调用一个不存在的函数。

  2. 如果从ClassB中删除foo,表达式ClassB::foo实际上是指继承于ClassBClassA::foo,所以在这里没有问题。

进一步阐述1:成员指针的工作方式与普通指针相反。对于普通指针,您可以将ClassB*分配到ClassA*中(因为所有的ClassB实例也都是ClassA实例),但不能反过来。对于成员指针,您可以将ClassA::*分配到ClassB::*中(因为ClassB包含了所有的ClassA成员),但不能反过来。


3

可以的。你不能将类A的函数指针分配给类B的函数指针,但可以进行以下操作:

fPtr = &ClassA::foo;
ClassB b;
classA* a = &b;
(a->*fPtr)();

当在 ClassB 中重写了 foo 函数时,将调用它。

ClassB 中没有 foo 函数时,将使用 ClassA 的函数。 实时示例


0
首先,我不是C++专家(我正在努力学习C++)。如果我错了,请纠正我,这是一个实验。
作为学习成员函数指针的一部分,我也在寻找一个调用基类成员函数指针来调用派生类成员函数的代码示例。在阅读了这个线程之后,我创建了一个代码示例,即使成员函数不是虚函数也可以调用派生成员函数。
我使用Delegator作为Invoker。我希望它对某个人有用。(如果我做错了什么,请纠正我)
class AbstractBase
{
protected:
    AbstractBase()
    {};

    virtual ~AbstractBase()
    {};

public:
    //Place holder method
    void SimpleMethod(void)
    {
        printf_s("\nAbstractBase::SimpleMethod");
    };
};

class Derived : public AbstractBase
{
public:
    //This overridden but not virtual.
    void SimpleMethod(void)
    {
        printf_s("Derived::SimpleMethod");
    };
};

typedef void (AbstractBase::*SimpleMethodCallback)();

class Delegator
{
private:
    AbstractBase * pbasePtr;
    SimpleMethodCallback pCallback;

public:
    Delegator()
    {
        this->pbasePtr = nullptr;
        this->pCallback = nullptr;
    };

    void RegisterCallback(AbstractBase * pbasePtr, SimpleMethodCallback callback)
    {
        if (pbasePtr == nullptr || callback == nullptr)
            throw "Delegate target or callback cannot be null";

        this->pbasePtr = pbasePtr;
        this->pCallback = callback;
    };

    void Invoke()
    {
        if (this->pbasePtr == nullptr || this->pCallback == nullptr)
            throw "Inoke cannot be performed on null target or callback";
        (pbasePtr->*pCallback)();
    }
};


int _tmain(int argc, _TCHAR* argv[])
{
    Delegator delegate;
    delegate.RegisterCallback(new Derived(), reinterpret_cast<SimpleMethodCallback>(&Derived::SimpleMethod));

    delegate.Invoke();

    return 0;
}

输出:

delegate.Invoke() 将调用 Derived::SimpleMethod(),并且输出将为 "Derived::SimpleMethod"**


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