[英]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.