2012-01-24 6 views
1

ルック:なぜboost :: bindが削除されたオブジェクトで動作していますか?このコードでは

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

#include <iostream> 
using namespace std; 

class acceptor 
{ 
private: 
    asio::ip::tcp::acceptor * a; 
    asio::io_service &_service; 
    asio::ip::tcp::endpoint ep; 
public: 
    acceptor(asio::io_service &service, unsigned int port) 
        :_service(service), ep(asio::ip::tcp::v4(), port) 
    { 
        try { 
            a = new asio::ip::tcp::acceptor(service, ep); 
        } 
        catch (asio::system_error &e) { 
            cout << e.what() << endl; 
        } 
        continueAccept(); 
    } 

    ~acceptor() { 
        delete a; 
        cout << " destroy " << endl; 
    } 

    void continueAccept() { 
        cout << "start accepting ..." << endl; 
        boost::shared_ptr<asio::ip::tcp::socket> ptr(new asio::ip::tcp::socket(_service)); 
        a->async_accept(*ptr, boost::bind(&acceptor::handleAccept, this, ptr, asio::placeholders::error)); 
    } 

    void handleAccept(
        boost::shared_ptr<asio::ip::tcp::socket> &socket, 
        const asio::error_code &e) 
    { 
        if (e == asio::error::operation_aborted) { 
            cout << "handler is called by error_code : " << e.message() << endl; 
        } 
    } 

    void close() { 
        cout << "close is called ..." << endl; 
        a->close(); 
    } 
}; 

int main(int argc, char *argv[]) { 
    asio::io_service service; 
    acceptor *aa = new acceptor(service, 8899); 

    service.poll(); 
    service.reset(); 

    delete aa; 

    service.poll(); 
    service.reset(); 

    return 0; 
} 

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

  • 開始...
  • Destoryは
  • ハンドラはERROR_CODEによって呼び出されます。操作は

を中止mainメソッドでaaオブジェクトを削除すると、aa :: dest :: aaのデストラクタのasio :: ip :: tcp :: acceptorオブジェクトは彼のクローズを呼び出し、非同期操作はasio :: error :: operation_abortedを使って呼び出します。

さて、削除した後、クラッシュか悪いの使用メモリを起こすことはできませんが、期待されているAA削除されたオブジェクトにacceptHandlerのようなメソッドを呼び出します。もちろん、valgrindメモリアナライザでプログラムをテストしてエラーが発生する可能性はありますが、エラーはありません。

質問:削除されたオブジェクトの機能をブーストで呼び出す際に、プログラムが正しく機能するのはなぜですか?

+2

ブーストに固有のものではないかもしれませんが、まだ「削除済みオブジェクトを使用できますか」という質問はたくさんありますか? – crashmstr

+0

私の質問に対するあなたの選択は「いいえ」であることは明らかです。 – softghost

答えて

5

why the program works currectly while calling a function on deleted object by boost?

これは、未定義の動作が動作する方法の1つです。オブジェクトを削除したら、そのオブジェクトにアクセスしないでください。そうした場合、あなたはUBを呼び出します。

プログラムが機能していると思われたため、あなたは不運でした。

+0

いいえ、私はvalgrindソフトウェアでプログラムを分析し、初期化されていないメモリは使用していないことに言及します。 (おそらく、あなたはそれがどのように動作するかを知っているかもしれません。それはmallocを再実装し、自分自身で自由になり、メモリリークを検出し、メモリを初期化しません)。 – softghost

4

無効なポインタを使用してメンバー関数を呼び出すと、未定義の動作が発生します。

handleAcceptは仮想ではなく、メンバー変数にはアクセスしないため、実際にはそのポインタをデリファレンスしないため、特定のコンパイラで生成されるコードの未定義の動作は、ポインタはまだ有効でした。これは、エラーが見つけにくいので、残念です。

this oneのようないくつかのAsioの例で示されている1つの可能性は、共有ポインタを持つacceptorクラスを管理することです。 enable_shared_from_this<acceptor>から継承することで、共有のポインタではなく、生thisポインタに受け入れる機能をバインドすることができます。

a->async_accept(*ptr, 
    boost::bind(&acceptor::handleAccept, 
       shared_from_this(),     // <--- not "this" 
       ptr, asio::placeholders::error)); 

その後、オブジェクトがhandleAccept完了するまで生きままになります。それ以上の非同期操作にshared_from_this()をバインドする場合はそれ以上の時間がかかります。

+0

それはまさに正しいので、私はこの方法でAsioを使用することができます。なぜなら、UBにすることはできないからです。 – softghost

+0

@softghost:問題を解決するための提案を追加しました。あなたのアプローチは、いくつかの小さな変更を加えて動作します。 –

+0

ISO C規格のどのセクションにこれを未定義の動作としてマークしてくれますか? –

1

削除されたメモリの使用は、未定義の動作とみなされます。

あなたが予想していたとおり、クラッシュする可能性がありますが、特に何か起こっているものは期待できません。

関連する問題