(我之前的一个问题有类似的措辞和例子,但问的是完全不同的东西。 在我询问方法的想法之前。现在我问的是如何获得特定的工作方法。)
我有一些订阅功能,它会在发生某些事情时调用我的回调。 (假设它是一个计时器,并且会在经过一定的毫秒数后向我传递一个对象。)在查看了 lambda、std:function
和 std:bind
之后,我认为指向方法的指针的解决方案性能更高且更易于编写(尤其是对于订阅者而言),但我不太明白最后一点。
这个例子有点反射(reflect)了我的项目:我们有一个框架,用 Foo 表示,它只写一次,我们会有很多子类在这里用 Bar 表示,它们将由具有更多领域知识但较少 C++ 知识的人编写.因此,我们希望对 SubscribeTimer()
的调用尽可能简单。最后,该应用程序是高性能的,我们希望消除堆使用,包括创建隐式 std::bind
对象等。
#include <iostream>
#include <functional>
using namespace std;
class Data { int i; };
class Foo {
public:
typedef void (Foo::*Timer_T)( Data* pd );
virtual void SubscribeTimer( int iMilliseconds, Timer_T pmethod );
virtual void SubscribeTimer( int iMilliseconds, std::function<void(Data*)> pfn ); // undesired
virtual void OnTimerA( Data* pd ) { cout << "Foo::OnTimerA called" << endl; };
};
void Foo::SubscribeTimer( int iMilliseconds, Timer_T pmethod ) {
Data d;
(this->*pmethod)( &d );
}
void Foo::SubscribeTimer( int iMilliseconds, std::function<void(Data*)> pfn ) { // undesired
Data d;
pfn( &d );
}
class Bar: public Foo {
public:
void Init();
virtual void OnTimerA( Data* pd ) { cout << "Bar::OnTimerA called" << endl; };
virtual void OnTimerB( Data* pd ) { cout << "Bar::OnTimerB called" << endl; };
};
void Bar::Init() {
// Works like I want it to: easy to subscribe, and high performance.
SubscribeTimer( 1000, &Foo::OnTimerA );
// What I'd like to do, but doesn't work.
//SubscribeTimer( 1000, &Bar::OnTimerB );
// Does exactly what I want except more complicated to write and I believe slower to run.
SubscribeTimer( 1000, std::bind( &Bar::OnTimerB, this, std::placeholders::_1 ) );
}
int main( int nArg, const char* apszArg[] ) {
Bar bar;
bar.Init();
}
正如预期的那样(如果您忽略了在 Init()
的调用中编写 Foo::
而不是 Bar::
的要求SubscribeTimer()
) 程序输出:
Bar::OnTimerA called (from the version of SubscribeTimer() I like)
Bar::OnTimerB called (from the version of SubscribeTimer() I think is too verbose/slow)
所以这个例子完美地工作并且做了我需要的......除了子类只能为父类(super class)想定义的方法名称注册处理程序,无论它们是否被定义。但实际上,子类可能希望为不同的事件注册许多处理程序,这些处理程序的名称是父类(super class)不知道的。
所以一句话:我如何将OnTimerB()
传递给SubscribeTimer()
的方法指针版本?我很乐意更改 Timer_T
定义、SubscribeTimer()
或其他内容。不过作为底线,子类的成员指针解决方案比std::function
实现更复杂,而且比std::function
实现。另一方面,父类(super class) 中增加的复杂性不是问题,因为它是一次性代码。
最佳答案
调用Bar::OnTimerB
, 你需要一个 Bar
类型的指针.因此,您需要实现某种类型删除。
class Foo {
public:
template <typename Derived>
void SubscribeTimer(int iMilliseconds, void (Derived::*pmethod)(Data* pd)) {
auto const self = static_cast<Derived*>(this);
Data d;
(self->*pmethod)(&d);
}
};
显然,如果稍后要调用回调,则需要以某种方式存储它。这就是std::function<>
为你抽象。你当然可以自己做,但是std::function<>
还不错,尤其是在具有小功能优化的平台上。
因此您可以执行 std::function
内部包装:
template <typename Derived>
void SubscribeTimer(int iMilliseconds, void (Derived::*pmethod)(Data* pd)) {
SubscribeTimer(iMilliseconds,
[self = static_cast<Derived*>(this), pmethod](Data* pd) {
(self->*pmethod)(pd);
});
}
关于c++ - C++11/14/17 中指向方法回调的指针?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59046383/