传递非静态成员函数作为参数

时间:2015-05-21 20:20:46

标签: c++

我有很多类X的实例,每个实例都包含另一个类CB(有时多个CB)。 CB类中有一个函数指针,在某个事件(点击,计时器等)之后将被调用。

如果我可以将实例成员函数传递给本地CB类实例(在这种情况下传递say()函数),那么这将是非常有用的,这样一旦CB触发了函数,它只会修改自己实例中的东西。

示例代码:

 #include <iostream>                                                                                                     

 class CB {                                                                                                
     void (*function)(void);                                                                                             

     void init(void (*function)(void)) {                                                                                 
         this->function = function;                                                                                      
     }                                                                                                                   
 };                                                                                                                      

class X {                                                                                                               
    private:                                                                                                            
        CB cb;                                                                                                          

    public:                                                                                                             
        void init() {                                                                                                   
            cb.init( &(this->say) ); //<- Here's the problem                                                            
        }                                                                                                               

        void say() {                                                                                                    
            std::cout << "Hello World!" << std::endl;                                                                   
        }                                                                                                               
};                                                                                                                      

int main() {                                                                                                            
    X x;                                                                                                                
    x.init();                                                                                                           
} 

因此,我的上述示例因第17行错误而失败:

no known conversion for argument 1 from ‘void (X::*)()’ to ‘void (*)()’

是否有可能以某种方式将实例本地say()函数传递给实例CBCB函数无法修改,不幸的是,其他类的其他类将使用它。

我已经在其他线程中读到,除非say()函数是static或存在于类外(这意味着它无法访问实例变量),否则这很可能是不可能的。

是否有可能按照我拥有的方式工作?如果不是最常见的解决方案(这似乎是很多人会遇到的问题)?

2 个答案:

答案 0 :(得分:0)

这是获得至少与此类似的东西的一种方法,但仍有效:

#include <iostream>
#include <functional>

class CB
{
public:
    std::function<void(void)> function;

    template <class F>
    CB(F function) : function(function) { }

    void operator()() { function(); }
};

class X
{
private:
    CB cb;

public:
    X() :cb([this] {say(); }) { }

    void say() { std::cout << "Hello World!\n";  }

    void operator()() { cb(); }
};

int main() {
    X x;
    x();
}

这里我重写了一点CB。其中一些与现有代码不兼容,但这是因为现有设计并不是一个好主意。现在你正在进行通常所说的两阶段初始化,你需要创建一个对象,在该对象真正可以使用之前调用它的init()。这通常是不受欢迎的。

我还改变了CB来存储std::function,它可用于存储指向函数的指针,几乎可以像函数一样调用的任何其他东西。< / p>

然后我添加了一个operator()来实际调用存储的类函数对象。

X中我也做了一些更改,最大的就是使用lambda表达式来生成一个可以像函数一样调用的对象。这在构造期间传递给CB的嵌入式实例。

所以顺序是外部代码创建一个X对象。这会创建一个CB类的嵌入式实例,并向其传递一个调用X::say的闭包。然后我们调用X::operator(),调用CB::operator(),调用X::say()

答案 1 :(得分:0)

  

“是否有可能以某种方式将实例本地say()函数传递给实例CB

如果您确定需要函数指针,请改用std::function包装器:

class CB {
    // void (*function)(void); Replace this with:
    std::function<void,void> fn_;
public:                                                                                  
    // void init(void (*function)(void)) { 
    //     this->function = function;
    // } Replace with a constructor
    CB(const std::function<void,void>& fn) : fn_(fn) {}
};

如果需要它将它绑定到特定的类实例,请使用std::bind

class X {
     CB cb_;
public:
    // Again use a constructor with a member initializer list
    void X() : cb_(std::bind(&X::say, this) {}

    void say() {
        std::cout << "Hello World!" << std::endl;
    }
};
  

“CB功能无法修改,不幸的是其他类别将使用它。”
  “我在其他线程中读到这很可能是不可能的,除非say()函数是静态的或存在于类的外面(这意味着它不能访问实例变量)。”

如果是这样,你有点失落。由于回调函数签名不提供一种用户数据参数,因此无法提供包含对特定实例的引用的包装器回调提供程序。

你的真实代码中的回调函数(我想你已经在你的问题中简化了它)真的有void fn(void);的签名,或者它是否提供了一些参数,允许绑定到任何数据实例,如void fn(void* userData);

您仍然可以提供static class X成员函数作为回调。如果say()函数没有更改X的状态(如示例中所示),将其更改为static实际上并不是问题(但我担心这不是你的问题)在这之后:

class CB {
    void (*fn_)(void);
public:
    CB(void (*fn)(void)) : fn_(fn) {}
};

class X {
     CB cb_;
public:
    // Again use a constructor with a member initializer list
    void X() : cb_(&X::say) {}

    static void say() {
 // ^^^^^^
        std::cout << "Hello World!" << std::endl;
    }
};