简体   繁体   中英

C++ member function pointers in class and subclass

I have one base class which holds a map for function pointers like this

typedef void (BaseClass::*event_t)();
class BaseClass {
    protected:
        std::map<std::string, event_t> events;
    public:
        // Example event
        void onFoo() {
            // can be added easily to the map
        }
};

Handling this works prefect, but now i want to make BaseClass an abstract base class to derive from like this:

 class SpecificClass : public BaseClass {
     public:
         void onBar() {
             // this is gonna be difficult!
         }
 };

Although i can access the map from SpecificClass i am not able to add onBar because the event_t type is only defined for the BaseClass ! Is there any possibility (maybe with templates?) which does not lead to define the event_t for each class i will use...

(It is not neccessary to use templates! Any good/suitable approach would be nice.)

More background information:

This whole thing is for a text based RPG. My base class could be called Location and the specifc one any location eg CivicCenter . Each Location object subscribes to my EventSystem which notifies all neccessary objects when i fire an event. Therefore i want to store in a map some pointers to private functions holding the actions with their "name" like onSetOnFire (xD) as the key.

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM