2015-05-21 153 views
3

我有很多类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或存在于类外(这意味着它不会访问实例变量)。

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

+0

你知道如何编写构造函数吗? –

+0

当'CB'调用试图修改其状态的方法时,最好希望你的'X'实例仍然存在。 – CoryKramer

+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,它可以用来存储指向一个功能,几乎什么都可以调用的功能等。

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

X我做了一些改变,最大的是使用lambda表达式来产生一个可以像函数一样调用的对象。这是在施工期间传递给CB的嵌入式实例。

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

+0

目前还不清楚'class CB'实际上是在添加什么值......看起来,人们可以摆脱它并使用'std :: function cb;' –

+0

@BenVoigt:很可能 - 我把它留在,猜测在现实生活中它可能会做更多/其他的事情。 –

0

"Is it possible to somehow pass an instances local say() function to the instances 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; 
    } 
}; 

"The CB function can't be modified and unfortunately will be used by other classes of a different type."
"I've read in other threads that this is most likely impossible unless the say() function is static or exists out side the class (which means it won't have access to an instances variables)."

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

是否在您的真正的代码(我想你已经在你的问题简化IT)真有void fn(void);签名,或者它提供了一些参数的回调函数,即允许其结合到像void fn(void* userData);任何数据实例?


您可能仍然能够提供的class X一个static成员函数作为回调。如果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; 
    } 
}; 
相关问题