我有很多类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()
函数传递给实例CB
? CB
函数无法修改,不幸的是,其他类的其他类将使用它。
我已经在其他线程中读到,除非say()
函数是static
或存在于类外(这意味着它无法访问实例变量),否则这很可能是不可能的。
是否有可能按照我拥有的方式工作?如果不是最常见的解决方案(这似乎是很多人会遇到的问题)?
答案 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;
}
};