Using base class function pointer to access derive

2020-07-22 09:55发布

问题:

I am using function pointer in my project, facing problem, created a test case to show it... below code fail with below error on MSVC2005 (in simple words i want to access dervied class function through base class function pointer)

error C2440: '=' : cannot convert from 'void (__thiscall ClassB::* )(void)' to '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;
}

My questions are

  1. Is it C++ behavior that I cant access derived class function through a base class function pointer or its a compiler bug?
  2. I have been playing with above case, if i comment out ClassB::foo, this code compile fine, without any further modification, Why is this so, should not fPtr = &ClassB::foo; again result in compile time error?

回答1:

  1. It's correct behaviour. Think of it this way: all instances of ClassB have the member ClassA::foo, but not all instances of ClassA have the member ClassB::foo; only those instances of ClassA which are actually the base class subobject of a ClassB instance have it. Therefore, assigning ClassB::foo into ClassAFoo and then using ClassAFoo in combination with a "pure" ClassA object would try to call a nonexistent function.

  2. If you remove foo from ClassB, the expression ClassB::foo acutally refers to ClassA::foo which is inherited in ClassB, so there's no problem there.

To elaborate on 1. further: pointers to members actually work the opposite way to normal pointers. With a normal pointer, you can assign ClassB* into ClassA* (because all instances of ClassB are also instances of ClassA), but not vice versa. With member pointers, you can assign ClassA::* into ClassB::* (because ClassB contains all the members of ClassA), but not vice versa.



回答2:

Yes, it's ok. You cannot assign to function pointer of class A function pointer of class B. You can do this

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

and overriden in ClassB function will be called.

When there is no function foo in ClassB, function of ClassA will be used. Live example



回答3:

First of all, I am not a C++ expert (I am trying to learn C++ so deep). So correct me if I am wrong and this an an experiment.

As part of learning about member function pointers, I was also searching for a code sample to call derived member using a base member function pointer. After going through this thread I created a code sample which is able to call derived member function even if member function is not a virtual.

I am using a Delegator to act as a Invoker. I hope it might helpful for some one. (Correct me If I am doing anything wrong)

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;
}

output:

delegate.Invoke() will call Derived::SimpleMethod() and the output will be "Derived::SimpleMethod"**