C++ member function pointers in class and subclass

佐手、 提交于 2019-12-05 05:34:09

This can't be done with your current map as it stands. Think about what would happen if you could put a child method into the map. Then you could pull a pointer-to-child-member (masquerading as base) out of the map, call it on a base class instance pointer, and then how would it call a derived class on a base class instance which obviously couldn't work.

Would a polymorphic approach work?

Yes; stop using member pointers.

The more correct way of doing what you want is to have an event type and an object pointer. So an event fires on a specific object. The event type would be a non-member function (or a static member). It would be passed the object pointer. And it would call some actual member function of that object.

Nowadays, the event type could be a std/boost::function. However, since the function parameters have to stay the same type for all events, this doesn't really fix your problem. You can't call SpecificClass::onBar from a BaseClass pointer unless you do a cast to a SpecificClass. And the event calling function would not know to do this. So you still can't put SpecificClass::onBar in the std/boost::function object; you still need some standalone function to do the cast for you.

This all just seems to be a terrible use of polymorphism. Why does SpecificClass need to derive from BaseClass at all? Can't they just be two unrelated classes?

After some thought and a redesign i was able to achieve what i wanted. Although i am stubborn and still using inheritance i have reimplemented the map. This is how it works now:

class Location {
    // ...

    protected:
        std::map<std::string, std::function<void(void)>> m_mEvents;
};

And now i can handle it like this:

class CivicCenter : public Location {
    public:
        CivicCenter() {
            // this is done by a macro which lookes better than this
            this->m_mEvents["onTriggerSomething"] =
                  std::bind(&CivicCenter::onTriggerSomething, this);
        }

        void onTriggerSomething() {
            // ...
        }

    // ...
};

With easy use of std::bind i am able to implement generic function pointers. When using parameters like in std::function<void(int, int)> remeber to use either boost's _1 and _2 or lambda expressions like me:

std::function<void(int,int)> f = [=](int a, int b) {
    this->anotherFunctionWithParams(a, b);
};

But this is just pointed out due to completeness of my solution.

You have to use static_cast:

event_t evt = static_cast<event_t>(&SpecificClass::onBar);

This is because it is slightly dangerous to cast to event_t, you could accidently apply it to a BaseClass instance.

How it works (for the skeptical):

class BaseClass {
public:
    typedef void (BaseClass::*callback_t)(); // callback method
    void doSomething(callback_t callback) {
        // some code
        this->*callback();
        // more code
    }
    void baseCallback(); // an example callback
};

class DerivedClass : public BaseClass {
public:
    void derivedCallback();
    void doWhatever() {
        // some code
        doSomething(&BaseClass::baseCallback);
        // more code
        doSomething(static_cast<callback_t>(&DerivedClass::derivedCallback));
        // et cetera
};

Here is what you should avoid, and why this is potentially dangerous:

void badCodeThatYouShouldNeverWrite()
{
    BaseClass x;
    // DO NOT DO THIS IT IS BAD
    x.doSomething(static_cast<callback_t>(&DerivedClass::derivedCallback));
}

The requirement for a static_cast makes it so you can't "accidentally" pass DerivedClass method pointers in. And if you think this is dangerous, just remember that it's a pointer, and pointers are always dangerous. Of course, there are ways you can do this that involve creating helper classes, but that requires a lot of extra code (possibly making a class for every function you want to pass as a callback). Or you could use closures in C++11, or something from Boost, but I realize that a lot of us do not have that option.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!