2012-01-24 73 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稱爲:操作中止

當我刪除主方法中的aa對象時,aa的析構函數上的asio :: ip :: tcp :: acceptor對象調用他的close,異步操作將調用asio :: error :: operation_aborted。

現在,經過刪除,呼叫像acceptHandler的方法AA刪除對象上不會引起崩潰或不好用的內存,但預計。當然,我使用valgrind內存分析器測試程序可能出現的錯誤,但不存在錯誤。

問題:爲什麼程序在通過boost調用已刪除對象的函數時正確地工作?

+2

也許沒有一個具體的提升,但是我們是否已經有很多「我仍然可以使用已刪除的對象」的問題? – crashmstr

+0

很明顯,您對我的問題的選擇是'不'。 – softghost

回答

5

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

因爲這是未定義行爲的作用方式之一。一旦你刪除了一個對象,你就不應該訪問它。如果你這樣做,你調用UB。

你是不吉利的,因爲你的程序似乎工作。

+0

不,我提到我使用valgrind軟件分析程序,並且沒有使用未初始化的內存。 (也許你知道valgrind是如何工作的:它重新實現malloc並釋放自己的內存並檢測內存泄漏並使用未初始化的內存)。 – softghost

4

通過無效指針調用成員函數會導致未定義的行爲。

在這種情況下,由於handleAccept不是虛擬的,並且不訪問任何成員變量,因此您的特定編譯器生成的代碼的未定義行爲恰好匹配將會發生的情況指針仍然有效。這很不幸,因爲它使錯誤更難找到。

一種可能性,在一些Asio示例中進行了說明,例如this one,是使用共享指針管理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

使用刪除的內存被認爲是未定義的行爲

您可能會像您期待的那樣發生崩潰,但您無法指望特別發生的任何事情。

相關問題