这令人困惑,但是基本上我所知道的是一个使用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
的回调。
在此示例中,令人讨厌的鬼对象只有一个空列表(大概是由于被破坏了),但是在我遇到此问题的实际程序中,它充满了垃圾内存并导致了分段错误。我还注意到调试器有些奇怪的行为。当它到达幽灵对象时,它不会只是跨步或越过,除非我在其中放置一个断点,否则它将跳过该函数。
这是怎么回事?为什么第二次回调没有正确绑定?在析构函数中我需要做些特别的事情吗?我是否缺少对函数指针或std::bind
的基本了解?
答案 0 :(得分:0)
这是怎么回事?
未定义的行为
为什么第二次回调没有正确绑定?
因为您创建了一个新的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