删除boost :: bind的原始指针参数

时间:2011-05-11 12:03:48

标签: c++ memory-management boost-bind auto-ptr boost-function

假设我有堆分配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”。其中一些是对象的原始指针。由于代码是旧的,我并不总是可以改变它。

编写自己的包装器以便在容器中存储回调是最后的手段(可能是唯一的),因此是赏金。

4 个答案:

答案 0 :(得分:8)

@pmjordan的想法已经朝着正确的方向发展。您回答说您无法使用shared_ptr,因为一旦构建,您就无法取回所有权。但这并不完全正确:使用shared_ptr的自定义删除机制,您可以。这是如何:

为您的Af(A*)

假设这些玩具定义
struct A {
    ~A() { std::cout << "~A()" << std::endl; }
};

void f( A * a ) {
    std::cout << "in f(A*)" << std::endl;
    delete a;
}
  1. 写一个可以“关闭”的删除器:

    struct opt_delete {
        bool m_delete;
        opt_delete() : m_delete( true ) {}
        template <typename T>
        void operator()( T * t ) {
            if ( m_delete ) delete t;
        }
    };
    
  2. 然后您可以编写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()覆盖了不支持的情况。

  3. 现在您可以手动换行f(A*),如下所示:

    void f_sp( const boost::shared_ptr<A> & a ) {
        f( take( a ) );
    }
    
  4. 最后,测试两种情况:

    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;
    }
    
  5. 使用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)

NB!这是UGLY!

刚刚尝试了一些概念验证。嗯,据我所知,它做了所要求的 - 但这些东西依赖于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);
}