假设我有堆分配A*
,我希望将其作为参数传递给boost::bind
。
保存boost::bind
以便稍后在boost::functions
的某个STL容器中进行处理。
我想确保在销毁STL容器时销毁A*
。
要说明:
A* pA = new A();
// some time later
container.push_back(boost::bind(&SomeClass::HandleA, this, pA);
// some time later
container is destroyed => pA is destroyed too
怎么做?
修改
也许我想要的不是那么现实。
我有原始指针和接收原始指针的函数。通过 boost :: bind 延迟通话。此时我想要自动内存管理,以防boost :: bind想要执行。我很懒,所以我想使用“准备好”的智能指针解决方案。
std :: auto_ptr 看起来像个好人,但是......
auto_ptr<A> pAutoA(pA);
container.push_back(boost::bind(&SomeClass::HandleA, this, pAutoA);
无法编译(请参阅here)
auto_ptr<A> pAutoA(pA);
container.push_back(boost::bind(&SomeClass::HandleA, this, boost::ref(pAutoA));
pAutoA被销毁,删除了潜在的pA。
编辑02
在上面提到的容器中,我需要使用不同的参数存储misc“callbacks”。其中一些是对象的原始指针。由于代码是旧的,我并不总是可以改变它。
编写自己的包装器以便在容器中存储回调是最后的手段(可能是唯一的),因此是赏金。
答案 0 :(得分:8)
@pmjordan的想法已经朝着正确的方向发展。您回答说您无法使用shared_ptr
,因为一旦构建,您就无法取回所有权。但这并不完全正确:使用shared_ptr
的自定义删除机制,您可以。这是如何:
为您的A
和f(A*)
struct A {
~A() { std::cout << "~A()" << std::endl; }
};
void f( A * a ) {
std::cout << "in f(A*)" << std::endl;
delete a;
}
写一个可以“关闭”的删除器:
struct opt_delete {
bool m_delete;
opt_delete() : m_delete( true ) {}
template <typename T>
void operator()( T * t ) {
if ( m_delete ) delete t;
}
};
然后您可以编写take()
函数,再次获得shared_ptr
有效负载的所有权:
template <typename T>
T * take( const boost::shared_ptr<T> & sp ) {
opt_delete * d = boost::get_deleter<opt_delete>( sp );
assert( d );
assert( d->m_delete == true );
d->m_delete = false;
return sp.get();
}
(这会将有效负载留在剩余的shared_ptr
个实例中,但对于您的情况,这没关系,而assert()
覆盖了不支持的情况。
现在您可以手动换行f(A*)
,如下所示:
void f_sp( const boost::shared_ptr<A> & a ) {
f( take( a ) );
}
最后,测试两种情况:
int main( int argc, char * argv[] ) {
const boost::shared_ptr<A> a( new A, opt_delete() );
const boost::function<void()> func =
boost::bind( &f_sp, a );
if ( argc >= 2 && *argv[1] == '1' ) // call 'func'
func();
else
; // don't
return 0;
}
使用1
参数执行测试程序将打印
在f(A *)中 〜A()
并且没有(或任何其他参数),它将打印
〜A()
您可以扩展测试工具以将func
放入容器中,但它仍然是安全的。在这种情况下唯一不安全的是多次调用func
个副本(但之后你会在take()
中触发第二个断言)。
编辑:请注意,此机制不是线程安全的。要使其具有线程安全性,您需要为opt_delete
提供互斥锁,以便将operator()
与take()
同步。
答案 1 :(得分:3)
我认为你的意思是你有一些功能,让我们称之为f()
,它需要A*
,然后你用boost::bind
代理?您可以更改此功能以接受Boost / TR1 shared_ptr<A>
吗?使用shared_ptr
(或者,不太可能是C ++ 98 std::auto_ptr
)应该可以解决您的生命周期问题。
或者,如果您无法更改f
本身,则可以创建一个接受shared_ptr<A>
的包装器,拉出原始指针并使用它调用f
。如果你发现自己编写了很多这些包装器,你可以创建一个模板来生成它们,假设函数签名是相似的。
答案 2 :(得分:1)
刚刚尝试了一些概念验证。嗯,据我所知,它做了所要求的 - 但这些东西依赖于const_cast假设。如果您决定在程序中使用类似的东西,请准备好一次性检查程序中发生的所有复制结构,并使用valgrind验证没有任何泄露/损坏。
Trick正在定义你自己的包装类,它忽略const限定符并允许从const引用的auto_ptr传递auto_ptr所有权。如果您尝试复制矢量本身,这可能会变得疯狂。
因此,请务必仔细阅读有关矢量复制语义,auto_ptr所有权转移语义的信息,最重要的是 - 只需使用shared_ptr:)
#include <iostream>
#include <boost/bind.hpp>
#include <algorithm>
#include <vector>
#include <boost/function.hpp>
class parameter_data
{
public:
~parameter_data()
{
std::cout << "~parameter_data()" << std::endl;
}
parameter_data()
{
std::cout << "parameter_data()" << std::endl;
}
};
void f( parameter_data* data )
{
std::cout << "Processing data..." << std::endl;
};
class storage_wrapper
{
private:
boost::function<void()> callable;
std::auto_ptr<parameter_data> data;
public:
storage_wrapper( const storage_wrapper& copy )
{
callable = const_cast< storage_wrapper&>(copy).callable;
data = const_cast< storage_wrapper&>(copy).data;
}
storage_wrapper( parameter_data *adata )
: data( adata )
{
callable = boost::bind( &f, adata );
}
storage_wrapper& operator=( const storage_wrapper& copy)
{
callable = const_cast< storage_wrapper&>(copy).callable;
data = const_cast< storage_wrapper&>(copy).data;
}
void operator()()
{
callable();
}
};
int main()
{
std::cout << "Start of program" << std::endl;
{
std::vector<storage_wrapper> container;
for ( int i = 0; i < 100; i++ )
container.push_back( storage_wrapper( new parameter_data() ) );
for ( int i = 0; i < 100; i++ )
container[i]();
}
std::cout << "End of program" << std::endl;
return 0;
}
答案 3 :(得分:1)
它不需要非常复杂:
class MyContainer : public std::vector<boost::function<void ()> > {
public:
void push_back(boost::function<void ()> f, A *pA)
{ push_back(f); vec.push_back(pA); }
~MyContainer()
{ int s=vec.size; for(int i=0;i<s;i++) delete vec[i]; }
private:
std::vector<A*> vec;
};
有一个问题需要通过MyContainer&amp; amp;而不是std :: vector引用,否则可以调用原始的push_back,它允许你可以在不提供A *指针的情况下push_back。此外,它没有检查绑定参数是否与pA相同的A *对象。您可以通过更改push_back原型来解决这个问题:
template<class T>
void push_back(T *object, void (T::*fptr)(), A *pA)
{
push_back(boost::bind(fptr, object, pA)); vec.push_back(pA);
}