6

假设我分配了堆A*,我想将其作为参数传递给boost::bind. boost::bind保存在一些 STL 中以供以后处理,例如boost::functions's 的容器。

我想确保A*将在销毁 STL 容器时被销毁。

演示:

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);

不编译(见这里

auto_ptr<A> pAutoA(pA);
container.push_back(boost::bind(&SomeClass::HandleA, this, boost::ref(pAutoA));

pAutoA 被破坏,删除底层 pA。

编辑 02

在提到的容器中,我需要存储带有不同参数的杂项“回调”。其中一些是指向对象的原始指针。由于代码很旧,我并不总是可以更改它。

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

4

4 回答 4

8

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

A为您的and假设这些玩具定义f(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()s 涵盖了不存在的情况)。

  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;
    }
    

使用参数执行测试程序1将打印

在 f(A*)
~A()

并且没有(或任何其他参数),它将打印

〜A()

您可以先扩展测试工具以放入func容器中,但它仍然是安全的。在这种情况下唯一不安全的是func多次调用副本(但随后您将触发 中的第二个断言take())。

编辑:请注意,这种机制不是线程安全的。为了使其线程安全,您需要提供opt_delete一个互斥锁以operator()take().

于 2011-05-15T17:54:19.430 回答
3

我假设你的意思是你有一些功能,让我们称之为它f()需要一个A*,然后你用它代理boost::bind?您可以更改此功能以接受 Boost/TR1shared_ptr<A>吗?使用shared_ptr(或不太可能的 C++98 std::auto_ptr)应该可以解决您的生命周期问题。

或者,如果你不能改变f自己,你可以创建一个接受 a 的包装器shared_ptr<A>,取出原始指针并f用它调用。如果你发现自己编写了很多这样的包装器,你可以创建一个模板来生成它们,假设函数签名是相似的。

于 2011-05-11T12:19:39.663 回答
1

注意!这是丑陋的!

刚刚草草了一些概念证明。好吧,据我所知,它可以满足要求-但是这些东西依赖于 const_cast 假设。如果你决定在你的程序中使用类似的东西,请准备好仔细检查程序中发生的所有复制结构,并使用 valgrind 来验证没有任何东西被泄露/损坏。

诀窍在于定义您自己的包装类,它忽略 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;
}
于 2011-05-11T15:34:06.043 回答
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 & 而不是 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);
} 
于 2011-05-15T18:21:55.723 回答