Об использовании (и полезности) указателя на функцию-член против прямого вызова функции-члена - PullRequest
0 голосов
/ 14 июля 2010

Я новичок в указании на функции-члены, и я хотел бы знать их плюсы и минусы.

В частности, рассмотрим это:

#include <iostream>
#include <list>

using namespace std;

class VariableContainer;

class Variable
{
public:
  Variable (int v) : value (v), cb_pointer (0) {}
  ~Variable () {}

  void SetCallback (void (VariableContainer::*cb)(int)) {
    cb_pointer = cb;
  }
  void FireCallback (void) {
    /* I need a reference to a VariableContainer object! */
  }

private:
  int value;
  void (VariableContainer::*cb_pointer) (int);
};

class VariableContainer
{
public:
  VariableContainer () {}
  ~VariableContainer () {}

  void AddVar (Variable &v) {
    v.SetCallback (&VariableContainer::Callback);
  }
  void Callback (int v) { cout << v << endl; }
};

int main ()
{
  Variable v (1);
  VariableContainer vc;

  vc.AddVar (v);
  v.FireCallback();

  return 0;
}

Как указано в комментарии, чтобывызвать обратный вызов (FireCallback) Мне нужна некоторая ссылка на существующий объект VariableContainer, который должен быть указан в качестве дополнительного аргумента VariableContainer::AddVar (...).Теперь:

  1. Должен ли я использовать указатель на функцию-член?Или мне следует позвонить напрямую Callback (...) (поскольку у меня есть указатель на объект VariableContainer)?
  2. Каковы преимущества и недостатки каждого решения?

TIA, Jir

Ответы [ 2 ]

2 голосов
/ 14 июля 2010

В зависимости от того, что будет меняться в будущем, вы можете решить:

  • Другие функции будут вызываться FireCallback переменной => указатель на mf может быть полезен, но другие механизмы более c ++ ish
  • Будет вызываться только функция 'Callback' => придерживаться прямого вызова arg->CallBack().

Возможное решение проблемы - использование интерфейсного уровня: это не более чем реализация шаблона Observer. Это немного больше ОО, поэтому многословно, но синтаксис намного проще.

class Observer {
public:
    virtual ~Observer(){};
    virtual void callback( int v ) = 0;
};

// actual implementation
class MyCallbackObserver : public Observer {
    virtual void callback( int v ) { std::cout << v << std::endl; }
    void some_other_method( int v ) { std::cout << "other " << v ; }
};

А у вашего Variable класса будет контейнер, полный наблюдателей:

class Variable {
public:
   std::vector<Observer*> observers; // warning: encapsulation omitted
   void FireCallback(){
      // assuming C++ 0x
      for( auto it : observers ) {
        (*it)->Callback( value );
      }
   }

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

class OtherCaller: public Observer {
 public:
     MyObserver* obs;
     virtual void callback( int v ) { obs->some_other_method( v ); }
}

И добавьте его в коллекцию:

Variable var;
MyObserver m;
OtherCaller mo;
mo.obs = &m;

var.observers.push_back(&m);
var.observers.push_back(&mo);

var.FireCallback();
1 голос
/ 14 июля 2010

Это зависит от ваших потребностей.

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

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

...