验证函子的目标对象

Posted

技术标签:

【中文标题】验证函子的目标对象【英文标题】:Validate a Functor's Target Object 【发布时间】:2014-07-14 18:27:03 【问题描述】:

我有一个类foo,它接受一个指向类bar的方法之一的成员函数指针,但是类bar的生命周期可能比foo短执行前是否存在?

目前,我正在尝试使用std::functionbool operator,但没有成功。

class Foo

public:
    void Foo::SetCallback( std::function< void( void ) > cb )
    
        _cb = cb; // My Bar class will call this to assign a Bar member function pointer to Foo's _cb member variable
    
    void Foo::CallCallback()
    
        if( _cb ) _cb(); // This evaluates to true even if the original bar object has been destroyed
        //I want this callback to only execute if the bar object exists
    
private:
    std::function< void( void ) > _cb;
;
class Bar

public:
    Bar( Foo* _foo )
    
        _foo->SetCallback( std::bind( &myCalledbackFunc, this );
    
    void myCalledbackFunc()
    
        //do stuff
    
;

【问题讨论】:

抱歉,您的问题我完全不清楚。 bar 到底是什么?请输入一个minimal sample,以重现您实际遇到的行为和错误。 除了仿函数之外还有一个 bar 实例的 weak_ptr 作为函数参数怎么样? @SilvesterSeredi 听起来是个好主意...我不熟悉std::weak_ptr 虽然我可以简单地在我的Bar ctor 中从this 创建一个吗? 您可以在 foo 中拥有 register_callbackderegister_callback 函数,并在 bar 类的析构函数中取消注册回调。 @perreal 这是我现有的临时解决方案......我只是希望 Functor 可以用来识别它引用的成员的类的存在。 【参考方案1】:

我想不出任何简单的方法来让 Foo 知道回调对象何时被销毁,但是如果你真的想从 Foo 类中验证对象是否还活着,我会在某个地方使用 weak_ptr路。如果您通过 new 创建 Bar 您可以改为通过 make_shared 创建实例,然后您会执行类似的操作(额外的成本是在每次构建 Bar 实例后调用一次函数):

class Bar;
class Foo

public:
    void Foo::SetCallback( std::function< void( void ) > cb )
    
        _cb = cb;
    

    void Foo::RegisterBar(std::weak_ptr<Bar> inbarPtr)
    
        barRef = inbarPtr;
    

    void Foo::CallCallback()
    
        if( _cb && !barRef.expired()) _cb(); 
    

private:
    std::function< void( void ) > _cb;
    std::weak_ptr<Bar> barRef;
;

class Bar

public:
    Bar( Foo* _foo )
    
        _foo->SetCallback( std::bind( &Bar::myCalledbackFunc, this ));
    
    void myCalledbackFunc()
    
        //do stuff
    
;

int main()

    Foo fooInstace;
    std::shared_ptr<Bar> barInstance = std::make_shared<Bar>(&fooInstace);
    fooInstace.RegisterBar(barInstance);
    return 0;

如果你真的坚持只改变 Bar 类,你可以使用这个丑陋、丑陋的 hack 和一个空的自定义删除器:

class Bar;
class Foo

public:
  void Foo::SetCallback( std::function< void( void ) > cb , std::weak_ptr<Bar> inbarPtr)
  
    _cb = cb;
    barRef = inbarPtr;
  
  void Foo::CallCallback()
  
    if( _cb && !barRef.expired()) _cb(); 
  
private:
  std::function< void( void ) > _cb;
  std::weak_ptr<Bar> barRef;
;


class Bar

  std::shared_ptr<Bar> selfPtr;
public:
  Bar( Foo* _foo )
  
    selfPtr = std::shared_ptr<Bar>(this, Bar::EmptyDeleter);
    _foo->SetCallback( std::bind( &Bar::myCalledbackFunc, this ), selfPtr);
  
  void myCalledbackFunc()
  
    //do stuff
  
protected:
  static void EmptyDeleter(Bar*)
  
  
;

【讨论】:

所以在我的代码中,我的Bar 示例类是由模板工厂创建的,工厂用std::unique_ptr 挂在它上面,并负责清理它。我无法更改工厂代码,因为这对设计的其余部分意味着什么。有没有办法只使用 Barinside 的代码来解决你的解决方案? 你不能把 unique_ptr 改成 shared_ptr 吗?改变应该是微不足道的 看看代码看起来像是一个相当大的变化......假设我可以做到......我仍然无法获得工厂持有我的@的std::shared_ptr Bar 类中的 987654329@ 对象。所以我不认为转换实际上能给我带来任何好处。 带有空自定义删除器的解决方案怎么样(我的答案中的第二个解决方案)? 伙计...我需要更好地理解这一点...这是说selfPtr 永远不会被清理吗? @perreal 的评论目前似乎更受欢迎。【参考方案2】:

在 C++ 中,对象使用的内存不会在对象销毁时归零。因此,正如Silvester Seredi 在他的回答中提到的那样,任何指针知道它引用的对象是否已被销毁的唯一方法是让指针引用拥有自动指针的对象。另外值得注意的是,一个对象不能在内部拥有,因此一个对象不能在本质上提供它是否已被销毁的指示符。

因此,唯一的解决方案是通知持有仿函数的类,在仿函数引用其成员的类被销毁时它是无效的,正如perreal 所建议的那样。请注意,这不是多线程安全的。

class Foo

public:
    void SetCallback( std::function< void( void ) > cb )
    
        _cb = cb;
    
    void UnSetCallback()
    
        _cb = std::function< void( void ) >();
    
    void CallCallback()
    
        if( _cb ) _cb();
    
private:
    std::function< void( void ) > _cb;
;
class Bar

public:
    Bar( Foo* foo ) : _foo( foo )
    
        _foo->SetCallback( std::bind( &myCalledbackFunc, this );
    
    ~Bar()
    
        _foo->UnSetCallback();
    
    void myCalledbackFunc()
    
        //do stuff
    
private:
    Foo _foo;
;

【讨论】:

以上是关于验证函子的目标对象的主要内容,如果未能解决你的问题,请参考以下文章

为啥这个函子的 operator() 需要尾随 const 修饰符?

Es6 函数式编程 MayBe函子的简单示例

预备篇 I :范畴与函子

进阶学习4:函数式编程FP——函子FunctorMayBe函子Either函子IO函子FolktalePointer函子Monad

函数式夜点心:异步流程与 Task 函子

JS函数式编程译5.2 函子 (Functors)