5

boost::functionとboost::bind(実際にはstd :: tr :: functionとbind)がまだ使用中に削除されている状況があります。これは安全ですか?私は通常それを避けますが、問題のあるコードは少し定着していて、私の唯一の他のオプションは新しいスレッドを追加することです。

typedef function<int(int)> foo_type;

foo_type* global_foo = NULL;

int actual_foo( int i, Magic* m )
{
    delete global_foo;
    return m->magic(i);
}

int main()
{
     Magic m;
    global_foo = new foo_type( bind( &actual_foo, _1, &m );

    return (*global_foo)(10)
}

バインドされたパラメーターは常にプレーンな整数型(実際のコードではintとポインター)であり、参照ではありません。

4

2 に答える 2

5

boost::functionまたはstd::tr1::functionsコピー可能なオブジェクトです。したがって、一般的に、それらを割り当てる理由はまったくありません。値で渡すだけです。

それらは実際のほとんどの場合に最適化されています...したがって、値で渡すだけです。

typedef function<int(int)> foo_type;

foo_type global_foo;

int actual_foo( int i, Magic* m )
{
   delete global_foo;
   return m->magic(i);
}

int main()
{
   Magic m;
   global_foo = bind( &actual_foo, _1, &m );

   return global_foo(10)
}

提案したコードは危険です。次のコードを実行してください。

    #include <boost/function.hpp>
    #include <boost/bind.hpp>
    #include <iostream>
    using namespace std;

    boost::function<void()> *glb;

    struct Data {
            int x;
            Data(int _x = 0) : x(_x) { cout<<"ctor:"<<this<<endl; }
            ~Data() { cout<<"dtor:"<<this<<endl; }
            Data(Data const &p) {x=p.x; cout<<"ctor:"<<this<<endl; }
            Data const &operator=(Data const &p) { x=p.x; cout<<this<<"="<<&p<<endl; return *this; }
    };

    void func(Data const &x)
    {
            delete glb;
            cout<<&x<<endl;
    }

    int main()
    {
            glb=new boost::function<void()>(boost::bind(func,Data(3)));

            (*glb)();
            return 0;
    }

funcに示されているデストラクタオブジェクト(同じポインタ値を持つdtor)にアクセスしようとすると、cout<<&x<<endlすでに呼び出されていることがわかります。

Data const &x関数オブジェクトを破棄すると、バインドされたパラメーターも破棄され、破壊されたために使用できなくなるためです。global_function

編集:コメントの明確化:

あなたがのようなものを持っているなら

map<string,function<void()> > calls;

void delete_key(){
    calls.erase("key");
}

main() 
{
     calls["key"]=delete_key;

     // Wrong and dangerous
     // You delete function while in use
     calls["key"]();

     // Correct and safe
     // You create a copy of function so it would not
     // be deleted while in use.
     function<void()> f=calls["key"];
     f();
}
于 2009-08-13T19:54:38.447 に答える
2

編集:この回答について (Charles Bailey と) もう少し議論しましたが、boost::function の実装に依存しているため、これは安全ではないと思います。

actual_foo に入ったときのコール スタックは次のようになります。

actual_foo
boost::function::operator()
main

したがって、actual_foo の実行が終了すると、 のoperator()コードに戻りboost::functionます。このオブジェクトは削除されています。これは問題であるとは限りません。呼び出しに少し似delete thisていますが、削除されたオブジェクトのメンバー関数で行うことには十分注意する必要があります。仮想関数を呼び出したり、データ メンバーを使用したりすることはできません。

問題は、それがラップする関数が呼び出された後に何をboost::functionするかについて何の保証もしていないことに気づいていないことです。operator()私のプラットフォームでは危険なことは何もしていないようです (そのため、valgrind は文句を言いません)。オブジェクトが削除された後は許可されません。たとえば、メンバー変数を使用してデバッグ情報を書き出す可能性があります。

したがって、これは特定の状況下で未定義の動作を引き起こす可能性がある危険なことだと思います。

さらに注意:

関数ポインターを呼び出した後、ブーストが実際に何をしているのかを簡単に調べました。ここを見る: http://boost.cvs.sourceforge.net/viewvc/boost/boost/boost/function/function_template.hpp?view=markup 687 行目の operator() 関数で、私の解釈では、すぐに戻り値であり、他には何もしないので、実際には、その実装で問題ないはずですが、それについてのコメントは依然として危険です. 間違ったコードを見つけたり、間違って理解したりする可能性が非常に高いことに注意してください...

以下の元の回答:

これでいいと思います。に入るactual_fooまでに、 オブジェクトboost::bindboost::functionオブジェクトはジョブを終了し、実際の関数 を実行していますactual_foo

valgrindを介してプログラムを実行することにより、プラットフォーム (gcc 4.2.4、Linux) でこれをチェックアウトしました。

これが私が実行したプログラムです:

#include <boost/bind.hpp>
#include <boost/function.hpp>

class Magic
{
public:
    int magic( int i )
    {
        return 5;
    }
};

typedef boost::function<int(int)> foo_type;

foo_type* global_foo = NULL;

int actual_foo( int i, Magic* m )
{
    delete global_foo;
    return m->magic(i);
}

int main()
{
    Magic m;
    global_foo = new foo_type( boost::bind( &actual_foo, _1, &m ) );

    return (*global_foo)(10);
}

valgrind の出力は次のとおりです。

$ valgrind ./boost_bind
==17606== Memcheck, a memory error detector.
==17606== Copyright (C) 2002-2007, and GNU GPL'd, by Julian Seward et al.
==17606== Using LibVEX rev 1804, a library for dynamic binary translation.
==17606== Copyright (C) 2004-2007, and GNU GPL'd, by OpenWorks LLP.
==17606== Using valgrind-3.3.0-Debian, a dynamic binary instrumentation framework.
==17606== Copyright (C) 2000-2007, and GNU GPL'd, by Julian Seward et al.
==17606== For more details, rerun with: -v
==17606== 
==17606== 
==17606== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 17 from 1)
==17606== malloc/free: in use at exit: 0 bytes in 0 blocks.
==17606== malloc/free: 1 allocs, 1 frees, 16 bytes allocated.
==17606== For counts of detected errors, rerun with: -v
==17606== All heap blocks were freed -- no leaks are possible.

しかし、これは奇妙なことだと言わざるを得ません。可能であれば、この関数オブジェクトをスタック変数にするか、スタック変数のデストラクタで削除する ( RAIIのように) ことにより、この関数オブジェクトの自動削除を使用することをお勧めします。それはよりきれいで、より安全で、怖くなく、より例外的に安全です。しかし、あなたはすでにそれをすべて知っていると確信しており、コードをそのまま使用する正当な理由があります。

于 2009-08-13T19:19:24.973 に答える