指向成员函数的指针的实际用途是什么?

sla*_*ais 13 c++ member-functions

我已经阅读了这篇文章,我从中得到的是,当你想调用指向成员函数的指针时,你需要一个实例(指向一个或一个堆栈引用的指针)并调用它:

(instance.*mem_func_ptr)(..)
or
(instance->*mem_func_ptr)(..)
Run Code Online (Sandbox Code Playgroud)

我的问题是基于这样的:既然你实例,为什么不直接调用成员函数,如下所示:

instance.mem_func(..) //or: instance->mem_func(..)
Run Code Online (Sandbox Code Playgroud)

指向成员函数的指针的合理/实际用途是什么?

[编辑]

我正在玩X-development并进入我正在实现小部件的阶段; 用于将X事件转换为我的类和小部件的事件循环线程需要在每个小部件/窗口的事件到达时为每个小部件/窗口启动线程; 为了做到这一点,我认为我需要在我的类中使用事件处理程序的函数指针.

不是这样的:我发现的是,通过简单地使用虚拟基类,我可以以更清晰和更整洁的方式做同样的事情.无需任何指向成员函数的指针.正是在开发上述内容时,出现了对成员函数指针的实际可用性/含义的疑问.

一个简单的事实是,您需要引用一个实例才能使用member-function-pointer,这样就不需要了.

[编辑 - @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");
}
Run Code Online (Sandbox Code Playgroud)

{edit] 上面例子中直接调用的潜在问题:
在Handler_THREE()中,方法的名称必须是硬编码的,强制在任何使用它的地方进行更改,以对方法应用任何更改.使用指向成员函数的指针,唯一需要进行的其他更改是创建指针的位置.

[编辑] 从答案中收集的实际用法:

来自Chubsdad的回答:
什么:专用的"来电者"功能用于调用mem-func-ptr;
好处:使用其他对象提供的功能保护代码
如何:如果在许多地方使用特定功能并且名称和/或参数发生变化,那么您只需要更改分配名称和/或参数的名称指针,并调整"呼叫者"功能中的呼叫.(如果该函数用作instance.function(),则必须在任何地方进行更改.)

来自Matthew Flaschen的回答:
什么:类中的本地特化
效益:使代码更清晰,更简单,更易于使用和维护
如何:替换传统上使用复杂逻辑实现的代码(可能)大的switch()/ if - 然后直接指向专业化的语句; 与上面的"来电者"功能非常相似.

Mat*_*hen 11

使用任何函数指针的原因相同:在调用函数指针之前,可以使用任意程序逻辑来设置函数指针变量.您可以使用开关,if/else,将其传递给函数,无论如何.

编辑:

问题中的示例确实表明您有时可以使用虚函数作为指向成员函数的指针的替代方法.这应该不足为奇,因为编程通常有多种方法.

这是一个虚拟函数可能没有意义的例子.与OP中的代码一样,这是为了说明,而不是特别现实.它显示了一个具有公共测试功能的类.这些功能使用内部私有功能.内部函数只能在设置后调用,之后必须调用拆解.

#include <iostream>

class MemberDemo;
typedef void (MemberDemo::*MemberDemoPtr)();

class MemberDemo
{
    public:
    void test1();
    void test2();

    private:
    void test1_internal();
    void test2_internal();
    void do_with_setup_teardown(MemberDemoPtr p);
};

void MemberDemo::test1()
{
    do_with_setup_teardown(&MemberDemo::test1_internal);
}

void MemberDemo::test2()
{
    do_with_setup_teardown(&MemberDemo::test2_internal);
}

void MemberDemo::test1_internal()
{
    std::cout << "Test1" << std::endl;
}

void MemberDemo::test2_internal()
{
    std::cout << "Test2" << std::endl;
}

void MemberDemo::do_with_setup_teardown(MemberDemoPtr mem_ptr)
{
    std::cout << "Setup" << std::endl;
    (this->*mem_ptr)();
    std::cout << "Teardown" << std::endl;
}

int main()
{
    MemberDemo m;
    m.test1();
    m.test2();
}
Run Code Online (Sandbox Code Playgroud)

  • @slash,再次,你也可以"直接"调用免费功能.常规和成员函数指针之间的考虑因素确实没有区别.可以使用参数调用常规函数指针(假设类型正确).您可以将实例视为一个额外参数(有时称为隐式参数). (3认同)

sbi*_*sbi 8

我的问题是基于这个:既然你有实例,为什么不直接调用成员函数[?]

Upfront:在超过15年的C++编程中,我使用成员指针可能是两次或三次.随着虚拟功能的出现,它并没有那么多用处.

如果要在对象(或许多对象)上调用某个成员函数,则必须使用它们,并且必须先决定调用哪个成员函数,然后才能找到要调用它的对象.是一个想要这样做的人的例子.

  • @slashmark:再次阅读,我甚至让重要的部分脱颖而出!当你想决定调用哪个成员函数时,你需要成员函数指针,然后才能决定调用它的对象_.在我链接的示例中,关于函数的决定是在`std :: for_each()`之外做出的,关于它内部对象的决定. (4认同)

Chu*_*dad 3

有很多实际用途。我想到的一个如下:

假设一个核心函数如下(适当定义了 myfoo 和 MFN)

void dosomething(myfoo &m, MFN f){   // m could also be passed by reference to 
                                     // const
   m.*f();
}
Run Code Online (Sandbox Code Playgroud)

这样的函数在存在指向成员函数的指针时,对扩展开放,对修改关闭(OCP

另请参阅Safe bool idiom,它巧妙地使用指向成员的指针。