C ++: указатель на функцию, которая содержит метод из конкретного экземпляра - PullRequest
0 голосов
/ 08 января 2019

Итак, я сейчас работаю над менеджером ввода для игры на C ++. Я думал о том, чтобы сделать это как map<char, list<Listener::method>>, чтобы при регистрации нажатой кнопки я вызывал все методы, зарегистрированные для конкретной клавиши.

Проблема в том, что я должен указать экземпляр, из которого я хочу вызвать метод. Есть ли способ хранить внутри переменной не только метод, но и экземпляр вызывающего объекта?

Что-то вроде:

class Foo 
{
public:
    Foo ();
    ~Foo ();
    void DoSomething();
};


Foo fooObject;

void(Foo::*fPtr)(void) = &fooObject::DoSomething;
(*fPtr)();

И это будет называться fooObject s DoSomething()

Возможно ли это?

Если нет, то может ли кто-нибудь указать мне на образец Event-Listener? Не должен быть слишком сложным или безопасным. Просто базовая структура.

Ответы [ 4 ]

0 голосов
/ 08 января 2019

Вы можете использовать bind -функцию, чтобы получить объект функции, параметры функции которого уже связаны с аргументами. Таким образом, вы можете привязать ссылку на объект, которая неявно является первым параметром в любой нестатической функции-члене, к соответствующему объекту, для которого вы хотите получить обратный вызов:

class Foo
{
public:
    int x;
    Foo (int _x) : x(_x) {} ;
    void doSomething() { cout << x << endl; }
};


int main() {

    Foo fooObject1(1);
    Foo fooObject2(2);

    std::function<void ()> fx1 = std::bind(&Foo::doSomething,&fooObject1);
    std::function<void ()> fx2 = std::bind(&Foo::doSomething,&fooObject2);

    std::vector<std::function<void ()>> callbacks;
    callbacks.push_back(fx1);
    callbacks.push_back(fx2);

    for (auto f : callbacks) {
        f();
    }
}

Выход:

1
2
0 голосов
/ 08 января 2019

Проблема с указателями на функции-члены заключается в том, что вам нужен экземпляр класса для их вызова. Вы не можете достичь этого только с помощью указателя, потому что указатель указывает только на функцию, а не на дополнительный объект, который нужно передать функции (помните, что все функции-члены имеют неявный первый параметр, который принимает тип класса).

Вы можете использовать std::function, чтобы иметь объект функции, который содержит вызываемый объект и вызываемую функцию для него. Если у вас есть

std::function<void()> func = [&](){ return fooObject.DoSomething(); }; // Bind with a reference.  Use this if the object will live as long as the function to save on a copy
std::function<void()> func = [=](){ return fooObject.DoSomething(); }; // Bind with a copy. Use this if the function will outlive the object

тогда, когда вы вызываете func с использованием func(), он запускает тело лямбды, которое вызывает DoSomething на fooObject.


Вы также можете использовать std::bind для привязки указателя на функцию-член к объекту, для которого он вызывается, но лямбды обычно предпочтительнее, так как синтаксис прост в использовании.

0 голосов
/ 08 января 2019

Есть ли способ хранить внутри переменной не только метод, но и экземпляр вызывающего объекта?

Да, вы можете связать метод и объект вместе.

С C ++ 11 у вас есть bind утилита.


Минимальный пример из вашей концепции:

struct Foo {
  void foo();
};

void bar() {
  // your map
  std::map<char, std::function<void()>> actions;

  // Bind method and object together inside fn
  Foo foo;
  auto fn = std::bind(&Foo::foo, &foo);

  // Insert fn into map
  actions.emplace('f', fn);

  // call fn from map! Be careful about object lifetime
  actions['f']();
}

Код Годболта


Будьте осторожны, потому что «объект связывания» (fn в моем примере) будет просто хранить ссылку на объект foo. Поэтому, если вы вызовете fn после того, как объект foo будет уничтожен, вы получите неопределенное поведение .


Если вы хотите позаботиться о времени жизни объекта, вы можете скопировать его с помощью лямбда-функции. Например:

auto fn = [foo]() { foo.foo(); }

Или используйте пользовательскую структуру и храните скопированный объект внутри.

0 голосов
/ 08 января 2019

Не существует такого понятия, как «метод из конкретного экземпляра».

  1. Вы можете держать указатель на функцию-член класса. Такие указатели не привязаны ни к какому конкретному объекту.
  2. Вы можете использовать этот указатель на функцию для любого объекта класса.

С учетом

class Foo 
{ 
  public:
    Foo ();
    ~Foo ();
    void DoSomething();
};

Любой класс может содержать указатель на функцию-член DoSomething (вы называете это методом).

void(Foo::*fPtr)(void) = &Foo::DoSomething;

Вам потребуется объект, чтобы иметь возможность вызывать функцию-член. Синтаксис вызова немного туповат, но здесь он есть.

Foo foo = <some function call>;
(foo.*fPtr)();
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...