验证函子的目标对象
Posted
技术标签:
【中文标题】验证函子的目标对象【英文标题】:Validate a Functor's Target Object 【发布时间】:2014-07-14 18:27:03 【问题描述】:我有一个类foo
,它接受一个指向类bar
的方法之一的成员函数指针,但是类bar的生命周期可能比foo
短执行前是否存在?
目前,我正在尝试使用std::function
的bool 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_callback
和 deregister_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
挂在它上面,并负责清理它。我无法更改工厂代码,因为这对设计的其余部分意味着什么。有没有办法只使用 Bar
类 inside 的代码来解决你的解决方案?
你不能把 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 修饰符?
进阶学习4:函数式编程FP——函子FunctorMayBe函子Either函子IO函子FolktalePointer函子Monad