问题描述
我最近一直在搞混C ++中的事件系统,例如boost :: signals2和类似的库。它们似乎都有一个共同的局限性:它们实际上与移动语义无关。
如果将信号连接到作为对象成员的插槽,则它引用该对象的当前内存地址以调用该插槽。一旦该地址发生更改(例如,由于对象所在的std :: vector必须重新分配其内存数组),连接就会中断。它仍然指的是移出的地址。
对于如何正确使用此类信号/插槽库,我有些困惑。您是否真的需要确保插槽永远不会更改其位置,例如将其放置在堆上?还是有一种方法可以使信号自动知道已更改的插槽位置?
解决方法
问题是插槽在内存中的位置不依赖于实例数据。成员函数作为类的所有实例的单个定义存在于内存中,并且不会更改其位置。当您为特定对象(例如object->member_func()
)调用函数时,“ this”指针与其他args隐式传递,因此该函数知道要调用哪个对象。 https://www.tutorialspoint.com/cplusplus/cpp_this_pointer.htm
以下是升压信号2信号用法的简单示例:
class Handler
{
private:
std::vector<std::string> array;
public:
void member_func(int value) {}
};
#include <boost/signals2.hpp>
void test()
{
boost::signals2::signal<void(int)> signal;
Handler object;
signal.connect(std::bind( &Handler::member_func,&object,std::placeholders::_1 ));
signal(5);
}
我们在绑定方法args中传递指向对象(&object
)的指针,因此当信号被调用时,boost将调用object.member_func();
,并且如果您将任何数据成员添加到Handler类或更改它们在运行时,这不会影响连接。
您唯一需要注意的是对象的寿命:删除对象之前必须断开插槽。否则,在调用信号时,object.member_func();
调用会导致未定义行为,因为该对象不再存在。