![](/img/trans.png)
[英]Registering a class member function as a callback to a function using std::bind
[英]Using std::bind and std::function with a class member causes the callback to use an old object reference?
这令人困惑,但是基本上我所拥有的是一个使用c ++ 11 std::function
和std::bind
具有回调函数的类。 一切正常。 但是,当我需要通过重新分配对象来重置所有内容时,似乎新对象并未使所有引用正确。 这是一个演示我的问题的示例程序:
#include <functional>
#include <list>
#include <iostream>
#include <string>
class OtherClass
{
public:
OtherClass() = default;
void RegisterCallback(std::function<void(void)> f) {
callback = f;
}
void PrintThings() {
callback();
}
std::function<void(void)> callback;
};
class MyClass
{
public:
MyClass() {
list_of_things.push_back("thing1");
list_of_things.push_back("thing2");
list_of_things.push_back("thing3");
list_of_things.push_back("thing4");
other_class.RegisterCallback(std::bind(&MyClass::MyFunction, this));
}
void PrintThings() {
MyFunction();
other_class.PrintThings();
}
void MyFunction() {
auto a = this;
for (auto& thing: list_of_things)
{
std::cout << thing << std::endl;
}
}
OtherClass other_class;
std::list<std::string> list_of_things;
};
int main()
{
MyClass my_class;
my_class.PrintThings();
my_class = MyClass();
my_class.PrintThings();
std::cout << "done" << std::endl;
}
这有点令人困惑,但是基本上,如果您使用调试标志进行编译并逐步执行调试,您会发现我们第一次调用my_class.PrintThings()
它将打印两次。 一次用于MyFunction()
调用,一次用于other_class.PrintThings()
调用,该调用将MyFunction
作为回调。 然后,我们用my_class = MyClass()
替换对象,并调用新的构造函数。 当我们逐步执行时,我们发现它在对MyFunction
的调用上打印列表, 而不在对other_class.PrintThings()
的调用上打印列表。 MyFunction
有一个变量a
,我用它来查看对象的地址。 第二次通过a
具有不同的地址,具体取决于是否将MyFunction
作为OtherClass
的回调进行OtherClass
。
在此示例中,令人讨厌的鬼对象只有一个空列表(大概是由于被破坏了),但是在我遇到此问题的实际程序中,它充满了垃圾内存并导致了分段错误。 我还注意到调试器有些奇怪的行为。 当它到达幽灵对象时,它不会只是进入或结束,除非我在其中放置一个断点,否则它将跳过该函数。
到底是怎么回事? 为什么第二次回调没有正确绑定? 在析构函数中我需要做些特别的事情吗? 我是否缺少对函数指针或std::bind
一些基本了解?
到底是怎么回事?
未定义的行为
为什么第二次回调没有正确绑定?
因为您创建了一个新的OtherClass作为将新的MyClass分配给my_class的一部分。 您尚未初始化其回调。
在析构函数中我需要做些特别的事情吗?
在析构函数中,分配和复制构造函数。 这是因为您要在自己中存储“我自己”的地址。 一切都很好,直到对象更改地址为止(复制时将更改地址)。 请注意,在下面的代码中,这三个都通过使用smart_ptr来处理。
一种解决方案是重构MyClass以使用pimpl习惯用法。 即,该类是地址永远不变的实现的包装。
#include <functional>
#include <iostream>
#include <list>
#include <string>
#include <memory>
class OtherClass
{
public:
OtherClass() = default;
void RegisterCallback(std::function<void(void)> f) {
callback = f;
}
void PrintThings() {
callback();
}
std::function<void(void)> callback;
};
class MyClass
{
struct Impl
{
Impl()
{
list_of_things.push_back("thing1");
list_of_things.push_back("thing2");
list_of_things.push_back("thing3");
list_of_things.push_back("thing4");
}
void MyFunction()
{
for (auto& thing: list_of_things)
{
std::cout << thing << std::endl;
}
}
void PrintThings() {
MyFunction();
other_class.PrintThings();
}
OtherClass other_class;
std::list<std::string> list_of_things;
};
std::unique_ptr<Impl> impl_;
public:
MyClass()
: impl_(std::make_unique<Impl>())
{
impl_->other_class.RegisterCallback(std::bind(&Impl::MyFunction, impl_.get()));
}
void PrintThings() {
impl_->PrintThings();
}
};
int main()
{
MyClass my_class;
my_class.PrintThings();
my_class = MyClass();
my_class.PrintThings();
std::cout << "done" << std::endl;
}
预期输出:
thing1
thing2
thing3
thing4
thing1
thing2
thing3
thing4
thing1
thing2
thing3
thing4
thing1
thing2
thing3
thing4
done
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.