Я прочитал эту статью, и я беру с нее то, что, когда вы хотите вызвать указатель на функцию-член, вам понадобится экземпляр (указатель на один или ссылку на стек) и назовите его так:
(instance.*mem_func_ptr)(..)
or
(instance->*mem_func_ptr)(..)
Мой вопрос основан на этом: поскольку у вас есть экземпляр, почему бы не вызвать функцию-член напрямую, например:
instance.mem_func(..) //or: instance->mem_func(..)
Какое рациональное/практическое использование указателей на функции-члены?
[править]
Я играю с X-разработкой и достиг стадии, когда я реализую виджеты; поток событий-цикла для перевода X-событий в мои классы и виджеты должен запускать потоки для каждого виджета/окна, когда событие для них прибывает; чтобы сделать это правильно, я думал, что мне нужны указатели функций для обработчиков событий в моих классах.
Не так: я обнаружил, что я мог бы сделать то же самое более ясным и аккуратным способом, просто используя виртуальный базовый класс. Нет необходимости в указаниях на функции-члены. При разработке вышесказанного возникло сомнение в практическом использовании/значении указателей на функции-члены.
Простой факт, что вам нужна ссылка на экземпляр для использования указателя-функции-члена, устаревает необходимость в нем.
[edit - @sbi и другие]
Вот пример программы для иллюстрации моей точки: (Обратите внимание: "Handle_THREE()" )
#include <iostream>
#include <string>
#include <map>
//-----------------------------------------------------------------------------
class Base
{
public:
~Base() {}
virtual void Handler(std::string sItem) = 0;
};
//-----------------------------------------------------------------------------
typedef void (Base::*memfunc)(std::string);
//-----------------------------------------------------------------------------
class Paper : public Base
{
public:
Paper() {}
~Paper() {}
virtual void Handler(std::string sItem) { std::cout << "Handling paper\n"; }
};
//-----------------------------------------------------------------------------
class Wood : public Base
{
public:
Wood() {}
~Wood() {}
virtual void Handler(std::string sItem) { std::cout << "Handling wood\n"; }
};
//-----------------------------------------------------------------------------
class Glass : public Base
{
public:
Glass() {}
~Glass() {}
virtual void Handler(std::string sItem) { std::cout << "Handling glass\n"; }
};
//-----------------------------------------------------------------------------
std::map< std::string, memfunc > handlers;
void AddHandler(std::string sItem, memfunc f) { handlers[sItem] = f; }
//-----------------------------------------------------------------------------
std::map< Base*, memfunc > available_ONE;
void AddAvailable_ONE(Base *p, memfunc f) { available_ONE[p] = f; }
//-----------------------------------------------------------------------------
std::map< std::string, Base* > available_TWO;
void AddAvailable_TWO(std::string sItem, Base *p) { available_TWO[sItem] = p; }
//-----------------------------------------------------------------------------
void Handle_ONE(std::string sItem)
{
memfunc f = handlers[sItem];
if (f)
{
std::map< Base*, memfunc >::iterator it;
Base *inst = NULL;
for (it=available_ONE.begin(); ((it != available_ONE.end()) && (inst==NULL)); it++)
{
if (it->second == f) inst = it->first;
}
if (inst) (inst->*f)(sItem);
else std::cout << "No instance of handler for: " << sItem << "\n";
}
else std::cout << "No handler for: " << sItem << "\n";
}
//-----------------------------------------------------------------------------
void Handle_TWO(std::string sItem)
{
memfunc f = handlers[sItem];
if (f)
{
Base *inst = available_TWO[sItem];
if (inst) (inst->*f)(sItem);
else std::cout << "No instance of handler for: " << sItem << "\n";
}
else std::cout << "No handler for: " << sItem << "\n";
}
//-----------------------------------------------------------------------------
void Handle_THREE(std::string sItem)
{
Base *inst = available_TWO[sItem];
if (inst) inst->Handler(sItem);
else std::cout << "No handler for: " << sItem << "\n";
}
//-----------------------------------------------------------------------------
int main()
{
Paper p;
Wood w;
Glass g;
AddHandler("Paper", (memfunc)(&Paper::Handler));
AddHandler("Wood", (memfunc)(&Wood::Handler));
AddHandler("Glass", (memfunc)(&Glass::Handler));
AddAvailable_ONE(&p, (memfunc)(&Paper::Handler));
AddAvailable_ONE(&g, (memfunc)(&Glass::Handler));
AddAvailable_TWO("Paper", &p);
AddAvailable_TWO("Glass", &g);
std::cout << "\nONE: (bug due to member-function address being relative to instance address)\n";
Handle_ONE("Paper");
Handle_ONE("Wood");
Handle_ONE("Glass");
Handle_ONE("Iron");
std::cout << "\nTWO:\n";
Handle_TWO("Paper");
Handle_TWO("Wood");
Handle_TWO("Glass");
Handle_TWO("Iron");
std::cout << "\nTHREE:\n";
Handle_THREE("Paper");
Handle_THREE("Wood");
Handle_THREE("Glass");
Handle_THREE("Iron");
}
{edit] Потенциальная проблема с прямым вызовом в приведенном выше примере:
В Handler_THREE() имя метода должно быть жестко закодировано, заставляя изменения быть внесенными в любом месте, где они используются, для применения любых изменений к методу. Используя указатель на функцию-член, единственным дополнительным изменением является создание указателя.
[править] Практическое использование, извлеченное из ответов:
Из ответ Chubsdad:
Что: выделенная функция "Caller" используется для вызова mem-func-ptr;
Преимущество: защищать код с помощью функций, предоставляемых другими объектами.
Как: Если конкретная функция (s ) используются во многих местах, а имя и/или параметры изменяются, тогда вам нужно только изменить имя, в котором оно выделено в качестве указателя, и адаптировать вызов в функции "Caller" . (Если функция используется как instance.function(), она должна быть изменена повсюду.)
Из ответ Мэтью Флашен:
Что: Локальная специализация в классе
Преимущества: Делает код намного понятнее, проще и проще в использовании и поддержке
Как: Заменяет код, который обычно реализуется с использованием сложной логики с (потенциально) большим переключателем()/if-then с прямыми указателями на специализацию; аналогично функции "Caller" выше.