Каково практическое использование указателей на функции-члены?

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

(instance.*mem_func_ptr)(..)
or
(instance->*mem_func_ptr)(..)

Мой вопрос основан на следующем: поскольку у вас есть экземпляр, почему не вызывать функцию-член напрямую, например:

instance.mem_func(..) //or: instance->mem_func(..)

Каково рациональное / практическое использование указателей на функции-члены?

[править]

I ' m играл с X-development и дошел до стадии, когда я внедряю виджеты; поток цикла событий для перевода X-событий в мои классы и виджеты должен запускать потоки для каждого виджета / окна, когда для них прибывает событие; чтобы сделать это правильно, я подумал, что мне нужны указатели на функции для обработчиков событий в моих классах.

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

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

[edit - @sbi & others]

Вот пример программы, чтобы проиллюстрировать мою точку зрения: (обратите внимание на '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 () имя метода должно быть жестко закодировано, поэтому изменения должны быть сделано везде, где он используется, чтобы применить любые изменения к методу. При использовании указателя на функцию-член единственное дополнительное изменение, которое необходимо сделать, - это место создания указателя.

[править] Практическое применение на основе ответов :

Из ответа Чубсдада :
{ {1}} Что: для вызова mem-func-ptr используется специальная функция «Caller»;
Преимущество: для защиты кода с помощью функций, предоставляемых другими объектами
Как: Если конкретная функция (s) используются во многих местах, и имя и / или параметры меняются, тогда вам нужно только изменить имя, где оно выделено как указатель, и адаптировать вызов в функции «Caller». (Если функция используется как instance.function (), ее нужно везде менять.)

Из ответ Мэтью Флашена :
Что: Локальная специализация в классе
Преимущество: делает код более ясным, простым и простым в использовании и обслуживании.
Как: заменяет код, который обычно реализуется с использованием сложной логики, на (потенциально) большие операторы switch () / if-then с прямыми указателями на специализация; довольно похожа на функцию "Caller" выше.

13
задан Community 23 May 2017 в 12:10
поделиться