【问题标题】:C++ Pointer to member function of an UNKNOWN CLASSC++ 指向未知类成员函数的指针
【发布时间】:2012-08-20 02:32:52
【问题描述】:

免责声明我不使用BOOST 或其他库

我终于了解了 PointerToMemberFunction 的工作原理。这是我的example code

#include <iostream>

using namespace std;

class Foo
{
        public:
                void foo ( )
                {
                        cout << "I'm a foo method\n";
                };
};

class Bar
{
        public:
                void bar ( Foo* fooPtr , void(Foo::*fooFnPtr)() )
                {
                        (fooPtr->*fooFnPtr)();
                };
};

int main()
{
        Foo* foo = new Foo();
        Bar* bar = new Bar();

        bar->bar ( foo , &Foo::foo );

        return 0;
}

现在,问题是什么。 Bar::bar 必须以某种方式进行修改,因为在实际项目中它不会知道fooFnPtr 是指向哪个类的指针。换句话说,Bar::bar 必须适用于任何类,而不仅仅是 Foo。我不知道,一个指向什么类的实例的指针被传递给Bar::bar

可以提供帮助的一件事是,与Bar::bar 一起工作的所有班级都是一个班级的孩子!

这可以实现吗?如何实现?如何修复我的代码?提前致谢!

【问题讨论】:

  • 使用std::function作为实参,传递实参时使用std::bind将成员函数与对象指针/引用/值绑定。
  • 这个问题有 10 亿次重复。

标签: c++ class pointers function-pointers iunknown


【解决方案1】:

您可以将bar 设为模板函数:

template<class T>
void bar ( T* fooPtr , void(T::*fooFnPtr)() )
{
    (fooPtr->*fooFnPtr)();
}

当然,如果你只想传递指向存在于公共基类中的成员的指针,你可以简单地这样做:

#include <iostream>

using namespace std;

class Foo
{
        public:
                virtual void foo ( )
                {
                        cout << "I'm a foo method\n";
                };
};

class Derived: public Foo
{
        public:
                virtual void foo ( )
                {
                        cout << "I'm a Derived method\n";
                };
};


class Bar
{
        public:
                void bar ( Foo* fooPtr , void(Foo::*fooFnPtr)() )
                {
                        (fooPtr->*fooFnPtr)();
                }
};

int main()
{
        Derived* derived = new Derived();
        Bar* bar = new Bar();

        bar->bar ( derived , &Foo::foo );

        return 0;
}

【讨论】:

  • +1 用于模板方法。关于继承的情况,只有当函数存在于base中时才会起作用,所以base必须提供完整的接口,不能只是一个标签
  • @DavidRodríguez-dribeas 是的,这就是我写 如果您只想将指针传递给公共基类中存在的成员的原因
  • 这个问题的唯一真正答案是std::function。模板很不错,但它只适用于可模板化的函数,并且不会采用 lambda 或其他函数对象。
  • @DeadMG 为什么你认为 OP 需要 lambdas 或函数对象?
  • @DeadMG:我非常不喜欢这样的空白语句,因为指向成员和函数指针的指针是正确使用的。请注意,问题不是建议人们避免使用它们,而是说明要始终避免使用它们。这对我来说类似于空白语句一个数组是一个指针,它适用于数组的大多数用途(任何用作右值)但会盲目误导人们。顺便说一句,您可能没有意识到您将标准库称为 crappy API
【解决方案2】:

我猜你是在使用闭包和委托,或者 C++ 中的等价物。简而言之,它们是可以调用的实例,并且包含指向您的类实例(或实例本身)的指针和指向那里的方法的指针。

看看std::bind。它们通常返回模板类的神秘实例,这些实例都可以转换为 std::function<...>,以防您需要使用统一类型的参数作为函数或方法的参数。

因此,您的代码将如下所示:

    void Bar::bar(std::function<void()> f)
    { 
       ....
    }

    Foo* foo = new Foo();
    Bar* bar = new Bar();
    auto f = std::bind( &Foo::foo, foo);
    bar->bar ( f );

当然,如果您决定不使用任何库,您仍然可以看看这些库以前是如何解决问题的,以便您以最好的方式重新发明*。

【讨论】:

  • std::functionstd::bind 是解决这个问题的唯一方法。
  • lambda 怎么样?我个人认为它们比绑定更容易阅读。
  • Lambda 很棒,但是——不考虑它们会像 bind 一样生成一个未命名的类型——我发现如果你正在做的只是将函数指针和实例绑定在一起,它们会掩盖意图。