简体   繁体   English

类和子类中的C ++成员函数指针

[英]C++ member function pointers in class and subclass

I have one base class which holds a map for function pointers like this 我有一个基类,它包含像这样的函数指针的map

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: 处理这个工作是完美的,但现在我想让BaseClass成为一个抽象基类来源于这样:

 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 ! 虽然我可以从SpecificClass访问地图,但我无法添加onBar因为event_t类型仅为BaseClass定义! Is there any possibility (maybe with templates?) which does not lead to define the event_t for each class i will use... 有没有可能(可能有模板?)不会导致为我将使用的每个类定义event_t ...

(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. 这一切都是基于文本的RPG。 My base class could be called Location and the specifc one any location eg CivicCenter . 我的基类可以称为Location和特定的任何位置,例如CivicCenter Each Location object subscribes to my EventSystem which notifies all neccessary objects when i fire an event. 每个Location对象都订阅我的EventSystem ,它在我触发事件时通知所有必要的对象。 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. 因此,我想在一个地图中存储一些指向私有函数的指针,这些函数用onSetOnFire (xD)作为键来保存带有“name”的动作。

This can't be done with your current map as it stands. 目前的map无法完成。 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 . 如今,事件类型可以是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 . 你不能叫SpecificClass::onBarBaseClass ,除非你做一个转换为指针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; 所以你仍然不能在std/boost::function对象中放置SpecificClass::onBar ; 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? 为什么SpecificClass需要从BaseClass派生? 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. 通过简单使用std::bind我可以实现泛型函数指针。 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)> remeber中的参数来使用boost的_1_2或像我这样的lambda表达式:

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 : 你必须使用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. 这是因为转换为event_t有点危险,您可能会意外地将其应用于BaseClass实例。

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. static_cast的要求使得你不能“意外地”传递DerivedClass方法指针。如果你认为这很危险,只要记住它是一个指针,指针总是危险的。 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. 或者您可以在C ++ 11中使用闭包,或者使用Boost中的闭包,但我意识到我们很多人都没有这个选项。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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