【发布时间】:2018-08-20 12:27:29
【问题描述】:
这令人困惑,但基本上我所拥有的是一个使用 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 的回调调用。
在这个例子中,有问题的 ghost 对象只有一个空列表(可能是被销毁的结果),但在我遇到的实际程序中,它被垃圾内存填满并导致分段错误。我还注意到我的调试器有一些奇怪的行为;当它到达幽灵对象时,它不会只是介入或越过,它会跳过该函数,除非我在其中放置断点。
发生了什么事?为什么第二次回调没有正确绑定?我需要在析构函数中做些什么特别的事情吗?我是否缺少对函数指针或std::bind 的一些基本理解?
【问题讨论】:
-
my_class = MyClass()调用副本,但您没有自定义赋值运算符,它将重新连接 other_class 的回调与分配对象。您看到未定义的行为。如果你在同一个对象中需要这种回调机制,你应该使用 pimpl idiom -
请添加预期和实际输出,我相信你的例子可以更短
-
@RichardHodges 实际上,编译器生成的复制赋值运算符将复制类的所有成员,从而复制 OtherClass 实例,从而也复制其 std::function 对象。另外,调用未初始化的 std::function 不是未定义的行为,它应该抛出 std::bad_function_call 异常。
-
@fkorsa 它将复制一个 std::function 引用实例的成员函数,该实例的成员函数在复制后将不存在
标签: c++ c++11 callback std-function stdbind