我在纯虚函数的帮助下设计了一个简单的callback-keyListener-“Interface”。我还使用了shared_ptr来表达所有权,并确保侦听器始终在处理程序中可用。这有点像魅力,但现在我想在std :: function的帮助下实现相同的功能,因为使用std :: function我可以使用lambdas / functors而我不需要从一些“接口” - 类派生出来。
我尝试在第二个示例中实现std :: function-variant,似乎可以工作,但我有两个与示例2相关的问题:
为什么这个例子仍然有用,虽然听众超出了范围? (看来,我们正在使用侦听器的副本而不是 origin 侦听器?)
如何修改第二个示例,以实现与第一个示例相同的功能(处理 origin 侦听器)? (member-ptr到std :: function似乎不起作用!当侦听器在处理程序之前超出范围时,我们如何处理这种情况?)
示例1:使用虚拟功能
#include <memory>
struct KeyListenerInterface
{
virtual ~KeyListenerInterface(){}
virtual void keyPressed(int k) = 0;
};
struct KeyListenerA : public KeyListenerInterface
{
void virtual keyPressed(int k) override {}
};
struct KeyHandler
{
std::shared_ptr<KeyListenerInterface> m_sptrkeyListener;
void registerKeyListener(std::shared_ptr<KeyListenerInterface> sptrkeyListener)
{
m_sptrkeyListener = sptrkeyListener;
}
void pressKey() { m_sptrkeyListener->keyPressed(42); }
};
int main()
{
KeyHandler oKeyHandler;
{
auto sptrKeyListener = std::make_shared<KeyListenerA>();
oKeyHandler.registerKeyListener(sptrKeyListener);
}
oKeyHandler.pressKey();
}
示例2:使用std :: function
#include <functional>
#include <memory>
struct KeyListenerA
{
void operator()(int k) {}
};
struct KeyHandler
{
std::function<void(int)> m_funcKeyListener;
void registerKeyListener(const std::function<void(int)> &funcKeyListener)
{
m_funcKeyListener = funcKeyListener;
}
void pressKey() { m_funcKeyListener(42); }
};
int main()
{
KeyHandler oKeyHandler;
{
KeyListenerA keyListener;
oKeyHandler.registerKeyListener(keyListener);
}
oKeyHandler.pressKey();
}
答案 0 :(得分:0)
std::function<Sig>
实现了值语义回调。
这意味着它会复制你输入的内容。
在C ++中,可以复制或移动的东西应该与原始版本很相似。你正在复制或移动的东西可以带有引用或指向一个extrenal资源的指针,一切都应该正常工作。
如何正确地适应价值语义取决于您在KeyListener中想要的状态;在你的情况下,没有状态,没有状态的副本都是相同的。
我假设我们想关心它所存储的状态:
struct KeyListenerA {
int* last_pressed = 0;
void operator()(int k) {if (last_pressed) *last_pressed = k;}
};
struct KeyHandler {
std::function<void(int)> m_funcKeyListener;
void registerKeyListener(std::function<void(int)> funcKeyListener) {
m_funcKeyListener = std::move(funcKeyListener);
}
void pressKey() { m_funcKeyListener(42); }
};
int main() {
KeyHandler oKeyHandler;
int last_pressed = -1;
{
KeyListenerA keyListener{&last_pressed};
oKeyHandler.registerKeyListener(keyListener);
}
oKeyHandler.pressKey();
std::cout << last_pressed << "\n"; // prints 42
}
或
{
oKeyHandler.registerKeyListener([&last_pressed](int k){last_pressed=k;});
}
这里我们存储一个引用或指向可调用状态的指针。这会被复制,并且在被调用时会发生正确的操作。
我与听众的问题是doulbe终身问题;只要广播者和接收者都存在,监听器链接才有效。
为此,我使用这样的东西:
using token = std::shared_ptr<void>;
template<class...Message>
struct broadcaster {
using reciever = std::function< void(Message...) >;
token attach( reciever r ) {
return attach(std::make_shared<reciever>(std::move(r)));
}
token attach( std::shared_ptr<reciever> r ) {
auto l = lock();
targets.push_back(r);
return r;
}
void operator()( Message... msg ) {
decltype(targets) tmp;
{
auto l = lock();
targets.erase(
std::remove_if( begin(targets), end(targets),
[](auto&& ptr){ return !(bool)ptr; }
),
end(targets)
);
tmp = targets;
}
for (auto&& f:tmp) {
if (f) f(msg...);
}
}
private:
std::mutex m;
auto lock() { return std::unique_lock<std::mutex>(m); }
std::vector< std::weak_ptr<reciever> > targets;
};
将您的代码转换为:
struct KeyHandler {
broadcaster<int> KeyPressed;
};
int main() {
KeyHandler oKeyHandler;
int last_pressed = -1;
token listen;
{
listen = oKeyHandler.KeyPressed.attach([&last_pressed](int k){last_pressed=k;});
}
oKeyHandler.KeyPressed(42);
std::cout << last_pressed << "\n"; // prints 42
listen = {}; // detach
oKeyHandler.KeyPressed(13);
std::cout << last_pressed << "\n"; // still prints 42
}