C++ 如何避免成员回调的循环引用&;共享ptr?

C++ 如何避免成员回调的循环引用&;共享ptr?,c++,c++11,callback,shared-ptr,C++,C++11,Callback,Shared Ptr,在我们的代码库中,有过多的情况如下: auto f = std::make_shared<Foo>(); auto b = std::make_shared<Bar>(); //void Foo::DoStuff( std::shared_ptr<Foo>, int ) auto func = std::bind( &Foo::DoStuff, f, std::_1 ); b->RegisterFunc( func ); auto f=std

在我们的代码库中,有过多的情况如下:

auto f = std::make_shared<Foo>();
auto b = std::make_shared<Bar>();
//void Foo::DoStuff( std::shared_ptr<Foo>, int ) 
auto func = std::bind( &Foo::DoStuff, f, std::_1 );
b->RegisterFunc( func );
auto f=std::make_shared();
自动b=std::使_共享();
//void Foo::DoStuff(std::shared_ptr,int)
auto-func=std::bind(&Foo::DoStuff,f,std:_1);
b->RegisterFunc(函数);
显然,将
shared_ptr
放入存储的回调中通常是个坏主意,因为它经常导致
f
永远不会被破坏。通常,您只需传入一个
弱\u ptr
,然后检查它在执行时是否仍然有效

不幸的是,
DoStuff()
修改
Foo
对象的内部状态,并且是一个成员函数,因此它要求指针有效以避免崩溃。这会导致循环引用,需要手动干预,如调用
Foo::Shutdown()
或其他方法来删除它们

有没有办法绕过存储成员函数回调的循环引用


注:就问题而言,我要求绑定函数为非静态成员函数。它在我们的代码中太普遍了,无法证明完全重写每个示例是正确的,即使它显然是设计疏忽。

正如您所说,您必须使用
弱\u ptr
并检查它是否有效<代码>弱\u ptr可用于创建指向对象的有效指针,因此您仍然可以对从
弱\u ptr
获取的指针调用
DoStuff()
。我不清楚你为什么认为
弱\u ptr
不起作用

这是您的示例,但现在它显示了一个潜在的循环引用,使用
weak\u ptr

#include <memory>
#include <functional>
#include <vector>
#include <cassert>

struct Foo {
    int x = 0;
    using callback_t = std::function<void()>;
    std::vector<callback_t> callbacks;

    void DoStuff() { x = 1; } //mutate some state
    void RegisterFunc(callback_t callback) { callbacks.push_back(std::move(callback)); }
};

using Bar = Foo;

template <class T>
auto wrap_shared_ptr_callback(const std::shared_ptr<T>& p, void(T::*f)()) {
    return [p = std::weak_ptr{ p }, f]() {
        auto ptr = p.lock();
        if (ptr) (*ptr.*f)();
    };
}

void create_circular_reference(std::shared_ptr<Foo> f, std::shared_ptr<Bar> b) {
    auto f_callback = wrap_shared_ptr_callback(f, &Foo::DoStuff);
    b->RegisterFunc( f_callback );
    auto b_callback = wrap_shared_ptr_callback(b, &Bar::DoStuff);
    f->RegisterFunc( b_callback );

    // test callbacks
    b->callbacks.front()();
    f->callbacks.front()();
}

int main() {
    auto f = std::make_shared<Foo>();
    auto b = std::make_shared<Bar>();

    // keep weak ptrs so we can check they've been destroyed correctly
    auto f_w = std::weak_ptr{ f };
    auto b_w = std::weak_ptr{ b };

    create_circular_reference(std::move(f), std::move(b));

    assert(f_w.expired());
    assert(b_w.expired());
}
#包括
#包括
#包括
#包括
结构Foo{
int x=0;
使用callback\u t=std::函数;
std::向量回调;
void DoStuff(){x=1;}//更改某些状态
void RegisterFunc(callback_t callback){callbacks.push_back(std::move(callback));}
};
使用Bar=Foo;
样板
自动换行共享ptr回调(const std::shared ptr&p,void(T::*f)(){
返回[p=std::弱_ptr{p},f](){
自动ptr=p.锁();
if(ptr)(*ptr.*f)();
};
}
无效创建循环引用(标准::共享ptr f,标准::共享ptr b){
自动f_callback=wrap_shared_ptr_callback(f,&Foo::DoStuff);
b->RegisterFunc(f_回调);
自动b_callback=wrap_shared_ptr_callback(b,&Bar::DoStuff);
f->RegisterFunc(b_回调);
//测试回调
b->callbacks.front();
f->callbacks.front();
}
int main(){
自动f=std::使_共享();
自动b=std::使_共享();
//保持弱PTR,以便我们检查它们是否已正确销毁
自动f_w=std::弱_ptr{f};
自动b_w=std::弱b_ptr{b};
创建循环参考(标准::移动(f),标准::移动(b));
断言(f_w.expired());
断言(b_w.expired());
}

对于上下文,代码在我们拥有的端到端系统上运行。大多数进程实际上只是在通电/断电时分配/销毁;这隐藏了问题(大部分)。如果没有,我们采用显式调用Foo::Shutdown方法。如果您通过lambda定义func,该lambda通过值捕获共享的\u ptr,它将在您销毁func时销毁,而不是之前销毁。循环引用在哪里
func
指的是
f
b
指的是
func
。。没有循环。在本例中,我猜您的意思是在从
a
b
的回调中包含一个引用,这将创建一个循环引用。如果是这样,您可能需要编辑您的问题以反映这一点