为什么boost :: bind与删除的对象有效?

时间:2012-01-24 14:02:25

标签: c++ boost memory-leaks boost-asio

看看这段代码:

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

输出是:

  • 开始记录...
  • destory
  • 处理程序由error_code调用:operation Aborted

当我删除main方法中的aa对象时,aa对象的析构函数上的asio :: ip :: tcp :: acceptor调用close,异步操作将调用asio :: error :: operation_aborted。

现在,在删除之后,在删除的对象上调用像acceptHandler这样的方法不会导致崩溃或使用内存不良,但是可以预期。当然,我使用valgrind内存分析器测试程序是否存在错误,但不存在错误。

问题:为什么程序在通过boost调用已删除对象上的函数时能够正常工作?

3 个答案:

答案 0 :(得分:5)

  

为什么程序在通过boost调用已删除对象上的函数时能够正常工作?

因为这是未定义行为的工作方式之一。删除对象后,您不应该访问它。如果这样做,则调用UB。

你不走运,因为你的程序似乎有用。

答案 1 :(得分:4)

通过无效指针调用成员函数会产生未定义的行为。

在这种情况下,没有任何实际取消引用该指针,因为handleAccept不是虚拟的,并且不访问任何成员变量,因此特定编译器生成的代码的未定义行为恰好与如果指针仍然有效,则会发生。这是不幸的,因为它使错误更难找到。

一些可能性,如this one等一些Asio示例所示,是用共享指针管理acceptor类;通过继承自enable_shared_from_this<acceptor>,您可以将accept函数绑定到共享指针,而不是原始this指针:

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

然后,对象将保持活动状态,直到handleAccept完成;如果将shared_from_this()绑定到进一步的异步操作,则更长。

答案 2 :(得分:1)

使用已删除的内存被视为未定义的行为

你可能会像你期望的那样崩溃,但你不能指望任何特别的事情发生。