в базовом классе, как определить контейнер, содержащий функцию obj, которая может быть любой функцией производного класса?

Я хочу определить контейнер в базовом классе, который содержит функцию obj или что-то еще, что может реализовать мою цель. Эти функции obj могут вызывать функции производных классов. все они принимают одинаковые параметры.

#include <vector>
#include <functional>
#include <iostream>


class Foo {
    Foo() {}
    virtual ~Foo(){}

    virtual void init()
    { registerCallback(0, &Foo::print_ori ); }
    void print_ori(int i) const { std::cout << i << '\n'; }

    void registerCallback(int key, ??? cb ) // NOT SURE HOW TO DEFINE THIS
    {
        callbacks[key] = cb;
    }

    void runCallbacks(int key, int n)
    {
        auto i = callbacks.find(key);
        if (i != callbacks.end()) {
            (*i)(*this, n);
        }
    }

    std::map<int, std::function<void(const Foo&, int) > > callbacks; // obviously, it's wrong. how to fix it?
};
struct Foo2 : public Foo {
    Foo2(int num) : Foo(num) {}
    virtual void init()
    {
        Foo::init();
        registerCallback(11, &Foo2::print1 );
        registerCallback(12, &Foo2::print2 );
    }
    void print1(int i) const { std::cout << " - Foo2.p1 - " << i << endl; }
    void print2(int i) const { std::cout << " - Foo2.p2 - " << i << endl; }
};



int main()
{
    Foo* obj = new Foo2();
    obj->init();
    obj->runCallbacks(12, 456);
}

person doufunao    schedule 06.08.2013    source источник
comment
В любом случае это не будет работать правильно, поскольку вы делаете UB, когда передаете fobj типа Foo в print_ttt и, таким образом, используете vtable типа Foo для вызова функции, которая находится только в Foo2.   -  person Mats Petersson    schedule 07.08.2013
comment
Есть ли какая-то конкретная причина, по которой вы не можете просто хранить кучу Foo (и производных) ссылок и вызывать одну и ту же виртуальную функцию для каждого объекта? Возможно, ваш пример слишком прост для того, что вы ДЕЙСТВИТЕЛЬНО хотите сделать, но похоже, что вы должны это сделать.   -  person Mats Petersson    schedule 07.08.2013
comment
ты прав. Но как я могу заставить его работать? Я хочу, чтобы базовый класс запускал набор зарегистрированных функций производного класса, которые принимают одинаковые параметры.   -  person doufunao    schedule 07.08.2013
comment
Похоже на вопрос XY, может быть, если мы немного отступим и вы опишете исходную проблему, вместо того, чтобы пытаться выработать свое предполагаемое решение, мы сможем найти решение...   -  person Mats Petersson    schedule 07.08.2013
comment
благодарю вас. Я пересмотрел свой пример.   -  person doufunao    schedule 07.08.2013
comment
Правильно, так что вы НЕ МОЖЕТЕ сделать это с виртуальной функцией, это точно - потому что это просто не вызовет правильную функцию. В общем, я думаю, если вы используете указатели на функции в коде, который также использует наследование [то есть virtual?], то что-то не так. Весь смысл наследования и виртуальных функций заключается в том, чтобы избежать указателей на функции и переложить ответственность за знание того, какие функции запускать на уровне - или что-то в этом роде. Я напишу ответ на этот счет, если вам это не нравится, пожалуйста, так и скажите...   -  person Mats Petersson    schedule 07.08.2013
comment
вы можете видеть, что я пытаюсь сохранить набор указателей на функции (я думаю, указатель на объект также необходим), определенных в производных классах, чтобы я мог запускать все зарегистрированные функции обратного вызова, вызывая base_ptr->runCallbacks(). количество обратных вызовов может быть десятками.   -  person doufunao    schedule 07.08.2013
comment
Я почти уверен, что вы пытаетесь создать какую-то обработку событий. Я также почти уверен, что вам ДЕЙСТВИТЕЛЬНО нужен не вектор функций обратного вызова для разных типов объектов. Это просто не будет работать таким образом. Вам нужно найти другой способ решения проблемы.   -  person Mats Petersson    schedule 07.08.2013


Ответы (1)


Вот способ добиться того, что ваш код выглядит так, как он пытается сделать, без использования указателей на функции:

class Foo {
    Foo() {}
    virtual ~Foo(){}

    void print_ori(int i) const { std::cout << i << '\n'; }

    virtual void do_runCallbacks(int v)
    {
    }

    void runCallbacks()
    {
        print_ori(3)
        do_runCallBacks(3);
    }

};
struct Foo2 : public Foo {
    Foo2(int num) : Foo(num) {}

    void do_runcallbacks(int v)
    {
       print1(v);
       print2(v);
    }
    void print1(int i) const { std::cout << " - Foo2.p1 - " << i << endl; }
    void print2(int i) const { std::cout << " - Foo2.p2 - " << i << endl; }
};



int main()
{
    Foo* obj = new Foo2();
    obj->runCallbacks();
}

Теперь вполне могут быть причины делать это совершенно по-другому, но я не понимаю, почему вам нужны как виртуальные функции, так и наследование, И объекты функций/указатели функций. Мне это кажется совершенно неправильным ("плохо пахнет")

Редактировать:

Вот что я придумал, что решает тип проблемы, которую вы описываете после редактирования исходного вопроса.

#include <iostream>
#include <map>

using namespace std;

class event_interface
{
public:
    virtual void action(int n) = 0;
};

class event_manager
{
public:
    event_manager(int n) : num(n) {}
    void register_event(int key, event_interface *eh) 
    { 
        handlers[key] = eh; 
    }
    void callback(int key)
    {
        auto h = handlers.find(key);
        if (h != handlers.end())
        {
        h->second->action(num);
        }
    }
private:
    map<int, event_interface *> handlers;
    int num;
};


class handler1 : public event_interface
{
public:
    void action(int n) { cout << "in handler1::action. n=" << n << endl; }
};

class handler2 : public event_interface
{
public:
    handler2(int n) : data(n) {}
    void action(int n) 
    { 
        cout << "in handler2::action. n=" << n 
         << " data = " << data << endl; 
    }
private:
    int data;
};

class multihandler 
{
private:
    class handler3: public event_interface
    {
    public:
    void action(int n) { cout << "in handler3::action. n=" << n << endl; }
    };

    class handler4: public event_interface
    {
    public:
    handler4(multihandler *m) : mh(m) {}
    void action(int n) 
        { 
        cout << "in handler4::action. n=" << n 
             << " data = " << mh->data << endl; 
        }
    private:
    multihandler* mh;
    };

public:
    multihandler(event_manager& em) : h4(this)
    {
        em.register_event(62, &h3);
        em.register_event(63, &h4);
        data = 42;
    }

private:
    handler3 h3;
    handler4 h4;
    int data;
};


int main()
{
    event_manager mgr(3);
    handler1 h1;
    handler2 h2(77);

    multihandler mh(mgr);

    mgr.register_event(12, &h1);
    mgr.register_event(13, &h2);

    int evts[] = { 12, 63, 62, 13, 18 };

    for(auto i : evts)
    {
    cout << "Event: " << i << endl;
    mgr.callback(i);
    }
}
person Mats Petersson    schedule 07.08.2013
comment
спасибо за код. но можете ли вы сделать это, используя функциональный объект или указатель? из-за некоторых деталей реализации я не могу сделать это по-вашему, потому что мне нужно использовать func ptr. причина 1: моя реальная проблема заключается в том, что не выполняются все обратные вызовы. база/вызывающий абонент должен выбрать правильный на основе ввода. (я снова пересмотрел пример. Извините :)) Причина 2: я хочу знать, может ли функциональный объект или ptr решить мою проблему. Я верю, что может, но я просто не понял этого. - person doufunao; 07.08.2013
comment
Я свяжусь с вами утром, здесь 1:30 ночи, и я не совсем проснулся, чтобы обдумать все различные последствия, но я думаю, что вам нужен класс интерфейса, который вы затем получаете свой фактический объект from, и делать реализацию именно так - все же неправильно (на мой взгляд) строить систему на основе указателей функций в C++ (как правило, исключения есть, но я не думаю, что это одно из них) . - person Mats Petersson; 07.08.2013
comment
Матс, спасибо за ответы! Не могли бы вы объяснить, почему использование указателей на функции неправильно? Мы часто используем его в нашей системе, и пока я не вижу существенных недостатков. Я хотел бы знать ваше мнение. Спасибо! - person doufunao; 07.08.2013
comment
Это не решение C++. Если вы когда-либо программировали на Python, возможно, вы знакомы с концепцией под названием Pythonic — правильный способ Python для решения проблемы. Указатели на функции хороши для некоторых вещей, но это не похоже на один из таких случаев. Очевидно, что в C++ даже больше, чем в Python, существует МНОГО разных способов добиться одного и того же. - person Mats Petersson; 07.08.2013
comment
вы правы, я занимаюсь обработкой событий. Одна из причин, по которой я не могу использовать виртуальную функцию, как вы показали, заключается в том, что я думал, что мои типы событий являются некоторыми классами шаблонов. но это было решено с другим подходом. Я пересмотрел свой код, применил вашу идею в вашем первом примере и удалил все указатели функций. новый код прошел все испытания. поэтому я считаю, что он работает правильно. спасибо! - person doufunao; 07.08.2013