【问题标题】: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 可以用来识别它引用的成员的类的存在。

标签: c++ functor member-function-pointers


【解决方案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;
};

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-04-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-04-06
    相关资源
    最近更新 更多