繁体   English   中英

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

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

我有一个基类,它包含像这样的函数指针的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
        }
};

处理这个工作是完美的,但现在我想让BaseClass成为一个抽象基类来源于这样:

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

虽然我可以从SpecificClass访问地图,但我无法添加onBar因为event_t类型仅为BaseClass定义! 有没有可能(可能有模板?)不会导致为我将使用的每个类定义event_t ...

(使用模板不是必需的!任何好的/合适的方法都会很好。)

更多背景资料:

这一切都是基于文本的RPG。 我的基类可以称为Location和特定的任何位置,例如CivicCenter 每个Location对象都订阅我的EventSystem ,它在我触发事件时通知所有必要的对象。 因此,我想在一个地图中存储一些指向私有函数的指针,这些函数用onSetOnFire (xD)作为键来保存带有“name”的动作。

目前的map无法完成。 想想如果你可以将一个子方法放入地图会发生什么。 然后你可以从地图中拉出一个指向子的成员(伪装成基础),在基类实例指针上调用它,然后如何在基类实例上调用派生类,这显然不能工作。

多态方法会起作用吗?

是; 停止使用成员指针。

做你想做的更正确的方法是拥有一个事件类型和一个对象指针。 因此,事件会触发特定对象。 事件类型将是非成员函数(或静态成员)。 它将传递给对象指针。 它会调用该对象的一些实际成员函数。

如今,事件类型可以是std/boost::function 但是,由于函数参数必须保持所有事件的相同类型,这并不能解决您的问题。 你不能叫SpecificClass::onBarBaseClass ,除非你做一个转换为指针SpecificClass 事件调用函数不会知道这样做。 所以你仍然不能在std/boost::function对象中放置SpecificClass::onBar ; 你仍然需要一些独立的功能来为你做演员表。

这一切似乎都是多态性的可怕用法。 为什么SpecificClass需要从BaseClass派生? 他们不能只是两个不相关的课程吗?

经过一番思考和重新设计,我能够实现我想要的。 虽然我很顽固,仍然使用继承,但我重新实现了地图。 这是它现在的工作方式:

class Location {
    // ...

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

现在我可以像这样处理它:

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() {
            // ...
        }

    // ...
};

通过简单使用std::bind我可以实现泛型函数指针。 当使用std::function<void(int, int)> remeber中的参数来使用boost的_1_2或像我这样的lambda表达式:

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

但由于我的解决方案的完整性,我们只是指出了这一点。

你必须使用static_cast

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

这是因为转换为event_t有点危险,您可能会意外地将其应用于BaseClass实例。

它是如何工作的(对于持怀疑态度的):

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

这是你应该避免的,以及为什么这有潜在危险:

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

static_cast的要求使得你不能“意外地”传递DerivedClass方法指针。如果你认为这很危险,只要记住它是一个指针,指针总是危险的。 当然,有一些方法可以做到这一点,包括创建辅助类,但这需要大量额外的代码(可能为你想要作为回调传递的每个函数创建一个类)。 或者您可以在C ++ 11中使用闭包,或者使用Boost中的闭包,但我意识到我们很多人都没有这个选项。

暂无
暂无

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

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