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

发布于 2024-12-06 23:35:50 字数 1037 浏览 0 评论 0原文

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

(没有必要使用模板!任何好的/合适的方法都是很好。)

更多背景信息:

这一切都是基于文本的角色扮演游戏。我的基类可以称为 Location ,具体的位置可以称为 CivicCenter 。每个 Location 对象都会订阅我的 EventSystem,当我触发事件时,它会通知所有必要的对象。因此,我想在映射中存储一些指向私有函数的指针,这些函数以其“名称”(例如 onSetOnFire (xD))作为键来保存操作。

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 e.g. 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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

诠释孤独 2024-12-13 23:35:50

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

多态方法有效吗?

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?

涫野音 2024-12-13 23:35:50

是的;停止使用成员指针。

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

如今,事件类型可以是 std/boost::function 。但是,由于所有事件的函数参数必须保持相同的类型,因此这并不能真正解决您的问题。除非转换为 SpecificClass,否则无法从 BaseClass 指针调用 SpecificClass::onBar。并且事件调用函数不知道要执行此操作。所以你仍然不能将SpecificClass::onBar放入std/boost::function对象中;您仍然需要一些独立的功能来为您进行转换。

这一切似乎都是多态性的糟糕使用。为什么 SpecificClass 需要从 BaseClass 派生?他们不能只是两个不相关的类吗?

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?

逆光下的微笑 2024-12-13 23:35:50

您必须使用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 中的某些东西,但我意识到我们很多人都没有这种选择。

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.

晨与橙与城 2024-12-13 23:35:50

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

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 等参数时,请记住使用 boost 的 _1_2 或 lambda 表达式像我一样:

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

但这只是由于我的解决方案的完整性而指出的。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文