将 std::bind 和 std::function 与 class 成员一起使用会导致回调使用旧对象引用?

Using std::bind and std::function with a class member causes the callback to use an old object reference?

这令人困惑,但基本上我得到的是一个 class,它具有使用 c++11 std::functionstd::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 的一些基本理解?

What is going on?

未定义的行为

Why isn't the callback bound properly the second time through?

因为您在将新的 MyClass 分配给 my_class 的过程中创建了一个新的 OtherClass。你还没有初始化它的回调。

Is there something special I need to do in the destructor or something?

在析构函数、赋值和复制构造函数中。这是因为您自己存储了 "myself" 的地址。这一切都很好,直到对象改变地址,它会在复制时改变地址。请注意,在下面的代码中,所有这三个都通过使用 smart_ptr.

来处理

一种解决方案是重构 MyClass 以使用 pimpl 习惯用法。即 class 是一个地址永远不会改变的实现的包装器。

#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