存档在 ‘boost’ 分类

异步编程时boost::bind中慎用this指针

2014年4月18日

在类的成员里有时候实现了异步操作,异步的回调函数仍为该类的成员函数,比如下面。

    async_write(m_socket, boost::asio::buffer(msg.c_str(), msg.size()),
        bind(&Games::HandSend, this,
            boost::asio::placeholders::error));

上面这个例子是一个很常见的boost.asio的异步执行代码,如果没认真看很容易忽略了bind参数中的“this”参数,异步执行过程中另一个线程会持有这个”this”指针并在未知的时间里回调HandSend方法,而在持有这个指针的时间内,这个线程并不知道当前对象有可能已经被销毁,所以造成很低级的悬空指针。

正确的做法应该是:

    async_write(m_socket, boost::asio::buffer(msg.c_str(), msg.size()),
        bind(&Games::HandSend, shared_from_this(),
            boost::asio::placeholders::error)
    );

在asio中使用shared_from_this智能指针,该使用的时候必须使用,否则几乎肯定会发生问题。

来分析分析什么时候该用this,什么使用该用shared_from_this()。

1.使用this的地方举例

1.1示例链接

在主函数中定义了tcp_server对象,这个对象会一直存在栈中,而对于每次accept的连接,都是new出来的tcp_connection。

  void start_accept()
  {
    tcp_connection::pointer new_connection =
      tcp_connection::create(acceptor_.get_io_service());

    acceptor_.async_accept(new_connection->socket(),
        boost::bind(&tcp_server::handle_accept, this, new_connection,
          boost::asio::placeholders::error));
  }

这里使用了一个工厂方法隐藏了new的代码,在bind中使用了this,因为tcp_server对象存在栈中,而且tcp_server对象并不是一个局部变量,不用担心他会在其他地方被释放掉。

1.2示例链接

server(boost::asio::io_service& io_service, short port)
    : io_service_(io_service),
      acceptor_(io_service, tcp::endpoint(tcp::v4(), port))
  {
    session_ptr new_session(new session(io_service_));
    acceptor_.async_accept(new_session->socket(),
        boost::bind(&server::handle_accept, this, new_session,
          boost::asio::placeholders::error));
  }

  void handle_accept(session_ptr new_session,
      const boost::system::error_code& error)
  {
    if (!error)
    {
      new_session->start();
    }

    new_session.reset(new session(io_service_));
    acceptor_.async_accept(new_session->socket(),
        boost::bind(&server::handle_accept, this, new_session,
          boost::asio::placeholders::error));
  }

类server中的两个函数和以上的解释一样。

2.未使用this的地方,使用share_from_this举例

以下的两个链接还是和上边两个链接分别相同,因为这两段程序中都使用了this与share_from_this,这能很好对比。

2.1示例链接,与1.1对比

void start()
  {
    message_ = make_daytime_string();

    boost::asio::async_write(socket_, boost::asio::buffer(message_),
        boost::bind(&tcp_connection::handle_write, shared_from_this(),
          boost::asio::placeholders::error,
          boost::asio::placeholders::bytes_transferred));
  }

tcp_server的bind中使用了this,而这个tcp_connection中使用share_from_this(),因为这个cp_connection对象是存在堆中,被new出来的。

 2.2示例链接,与1.2对比

void start()
  {
    using namespace std; // For time_t, time and ctime.
    time_t now = time(0);
    shared_const_buffer buffer(ctime(&now));
    boost::asio::async_write(socket_, buffer,
        boost::bind(&session::handle_write, shared_from_this()));
  }

server的bind中使用了this,而这个session中使用share_from_this(),因为这个session对象是存在堆中,被new出来的。

结论:

1.如果对象是存在于栈中则必须使用this,否则出现bad_weak_ptr异常。
2.如果对象存在于堆中则尽量使用share_from_this(),使用this的前提是你能保证这个对象一直存在(比如全局对象,主函数中具有全局生命的对象,静态对象)

boost智能指针源代码分析

2014年4月17日

首先来看shared_ptr.h

inline void _sp_set_shared_from_this( ... )
{
}
template<typename T> inline void _sp_set_shared_from_this(shared_ptr<T>* sp, enable_shared_from_this<T>* p)
{
	if (p)
	{
		p->_internal_accept_owner( *sp );
	}
}

这里对_sp_set_shared_from_this函数进行了重载,目的就是区分shared_ptr<>管理的对象是否继承自enable_shared_from_this.

class classA
{

};

class classB : enable_shared_from_this<classB>
{

};

当定义为shared_ptr<classA> 时则调用的是第一个函数,当定义为shared_ptr<classB>时则调用第二个函数。

那么什么时候调用_sp_set_shared_from_this呢?第二个函数的作用是什么呢?

1.先来看什么时候调用

shared_ptr(T * p = NULL)
	{
		m_ptr = p;
		m_count = new sp_counted_base(1,1);
		_sp_set_shared_from_this(this, m_ptr);
	}

	shared_ptr(const weak_ptr<T>& right)
	{
		m_ptr = right.get();
		m_count = right._get_sp();
		addref();
		_sp_set_shared_from_this(this, m_ptr);
	}

	shared_ptr(const this_type& o)
	{
		m_ptr = o.m_ptr;
		m_count = o.m_count;
		addref();
		_sp_set_shared_from_this(this, m_ptr);
	}

不只是上边的构造函数,赋值与复制操作符都会调用。具体的调用_sp_set_shared_from_this哪个重载版本函数,就是依靠m_ptr来推断。

2._sp_set_shared_from_this函数有什么用呢?

由前面的说明,已经知道能够调用_sp_set_shared_from_this实际有操作的类都是从enable_shared_from_this继承而来,在其内部实际上委托给了enable_shared_from_this基类

p->_internal_accept_owner( *sp );

这个函数的作用,实际上就是初始化了weak_ptr智能指针,先列出enable_shared_from_this这个类

template<typename T>
class enable_shared_from_this
{
public:
	shared_ptr<T> shared_from_this()
	{
		return shared_ptr<T>( weak_this_ );
	}
public:
	void _internal_accept_owner(const shared_ptr<T>& p)
	{
		weak_this_ = p;
	}
private:
	weak_ptr<T> weak_this_;
};

weak_ptr智能指针weak_this_的作用就是对shared_ptr进行观测,来看是怎么观测,由上边的_internal_accept_owner函数可以看出,实际上有如下的构造

	weak_ptr(const shared_ptr<T>& right)
	{
		m_ptr = right.get();
		m_count = shared_ptr<T>::_get_counted(right);
		m_count->weak.inc();
	}

这里可以看出weak_ptr智能指针,仅仅增加了观测,而没有对shared_ptr的计数器做任何修改。

综上,sp_set_shared_from_this函数就是为保存T对象的智能指针shared_ptr<T>增加观测智能指针weak_ptr<T>,并初始化。也说明了weak_ptr是依靠shared_ptr而成立的。

3.来看为什么上说有的类继承自enable_shared_from_this,为什么要继承自他呢?

继承自他的原因是想使用enable_shared_from_this的以下成员函数,继承他就可以让this变身为shared_ptr

	shared_ptr<T> shared_from_this()
	{
		return shared_ptr<T>( weak_this_ );
	}

来看下面的例子

class classC {
public:
    classC(): x_(4) {
        cout << "classC::classC()" << endl;
    }

    ~classC() {
        cout << "classC::~classC()" << endl;
    }

    void f() {
        shared_ptr<classC> p(this);
        cout << p->x_ << endl;
    }

private:
    int x_;
};

int main(int argc, char** argv) {
    shared_ptr<classC> x(new classC);
    x->f();
    return 0;
}

这段代码,将会两次析构 ,结果是灾难性的。甚至有的函数传入thsi指针后会被不小心delete,导致悬垂指针。

但是,我们将上边的部分代码修改成以下的

    void f() {
        shared_ptr<classC> p = shared_from_this();
        cout << p->x_ << endl;
    }

这样就正确了,这里演示的比较简单,在boost中,使用function,bind等的时候更容易出错。

4.weak_ptr如何使用

weak_ptr 不控制对象的生命期,但是它知道对象是否还活着。如果对象还活着,那么它可以提升 为有效的 shared_ptr;如果对象已经死了,提升会失败,返回一个空的 shared_ptr。此行为是线程安全的 。要想使用waek_ptr观测的智能指针share_ptr,必须使用以下的函数

	shared_ptr<T> lock() const
	{
		if ( !expired() )
		{
			return shared_ptr<T>(*this);
		}
		return shared_ptr<T>(NULL);
	}

对观测的shared_ptr增加一个引用计数,然后返回shared_ptr,这样就不会出现多次释放或者悬垂指针了。

异步时使用局部变量注意点

2014年4月17日

在类udp_server中,有下面一段异步接收数据的代码。

		socket_.async_receive_from(
			boost::asio::buffer(recv_buffer_), remote_endpoint_,
			boost::bind(&udp_server::handle_receive, this,
			boost::asio::placeholders::error));

在处理函数handle_receive中,需要将当前的日期字符串发给客户端,异步编程一般都需要使用bind来实现回调,以下为正确的代码:

	void handle_receive(const boost::system::error_code& error)
	{
		if (!error || error == boost::asio::error::message_size)
		{
			boost::shared_ptr<std::string> message(
				new std::string(make_daytime_string()));
			socket_.async_send_to(boost::asio::buffer(*message), remote_endpoint_,
				boost::bind(&udp_server::handle_send, this, message));
			start_receive();
		}
	}

但是往往在非异步编程中,我们对message的处理并不会用智能指针来包裹,而是在栈上定义一个临时变量,比如

	void handle_receive(const boost::system::error_code& error)
	{
		if (!error || error == boost::asio::error::message_size)
		{
			std::string message = make_daytime_string();
			socket_.async_send_to(boost::asio::buffer(message), remote_endpoint_,
				boost::bind(&udp_server::handle_send, this, message));
			start_receive();
		}
	}

在handle_receive函数结构后,栈上的对象message就会被析构,但是异步asyc_仍旧在使用它,所以会出错。

我们可以有几种解决办法:

1.使用文章头部的代码,用智能指针。

2.将message定义成类的数据成员。

boost::bind不同参数形式性能分析

2014年4月16日

boost::bind针对于值,引用,指针提供了统一的语法使用,可以利用这一特点,简化编码,来看下面的代码:

#include <boost/shared_ptr.hpp>
#include <boost/bind.hpp>
#include <iostream>

class A
{
public:
	A(){std::cout<<"A构造函数"<<std::endl;}
	A(const A& a) {std::cout<<"A拷贝构造函数"<<std::endl;}
	A& operator=(const A& a){std::cout<<"A赋值函数"<<std::endl;}
	~A(){std::cout<<"A析构函数"<<std::endl;}
	void printA(){}
};

int _tmain(int argc, _TCHAR* argv[])
{
	{
		A a;
		boost::bind(&A::printA, a)(); //1
		boost::bind(&A::printA, boost::ref(a))();//2
		boost::bind(&A::printA, &a)();//3
	}
	system("pause");;
	return 0;
}

使用1的时候,有多次的拷贝构造函数调用,因为bind是值语义的。

使用2,3的时候,则对A的对象只有一次构造和一次析构。

大约可以得出:在类A拷贝比较复杂或者耗时的情况下,最好还是使用引用或者指针方式传递。否则还是以值语义来传递。

boost::asio::ip::tcp::resolver解析

2014年4月16日

Resolver是Asio的域名解析系统,它将指定的URL和服务转化为相应的ip和port的endpoint端点。

query指定主机与服务,比如

tcp::resolver::query query("www.unix8.net", "http");

endpoint指定主机IP地址与端口,比如

boost::asio::ip::address add;  
add = boost::asio::ip::address::from_string("222.216.160.69");
tcp::endpoint endpoint(add, 80);

//或者
tcp::endpoint endpoint(boost::asio::ip::address::from_string("222.216.160.69"), 80);

切记更新endpoint的第一个参数不能使用如下的方式

boost::asio::ip::address add;  
add.from_string("222.216.160.69");
tcp::endpoint endpoint(add, 80);

而要使用

boost::asio::ip::address add;  
add = add.from_string("222.216.160.69");
tcp::endpoint endpoint(add, 80);

或者上边说的方法,这是因为非静态的from_string返回的是一个临时对象,并不会修改add。

1.同步解析

同步解析共4个重载,返回值都为endpoint列表的头迭代器。

  1. 重载1,根据query来解析,未指定error_code
    iterator resolve(const query & q);

    出错会抛出异常。

  2. 重载2,指定query,指定error_cod
    iterator resolve(
        const query & q,
        boost::system::error_code & ec);

    出错会置error_code。
    以下两个重载函数为逆向解析。

  3. 重载3
    iterator resolve(
        const endpoint_type & e);

    根据endpoint逆向解析,出错抛出异常

  4. 重载4
    iterator resolve(
        const endpoint_type & e,
        boost::system::error_code & ec);

    根据endpoint逆向解析,出错置error_code.
    来看同步解析的例子

    #include "stdafx.h"
    #include <iostream>
    #include <boost/array.hpp>
    #include <boost/asio.hpp>
    #include <boost/bind.hpp>
    using boost::asio::ip::tcp;
    
    int main()
    {
    	try
    	{
    		boost::asio::io_service io_service;
    		tcp::resolver resolver(io_service);
    		tcp::resolver::query query("www.unix8.net", "http");
    		boost::system::error_code e;
    		tcp::resolver::iterator iter = resolver.resolve(query, e);
    		tcp::resolver::iterator end;
    		while (iter != end)
    		{
    			tcp::endpoint ep = *iter++;
    			std::cout<<ep<<std::endl;
    		}
    	}
    	catch (std::exception& e)
    	{
    		std::cerr << e.what() << std::endl;
    	}
    	system("pause");
    	return 0;
    }

2.异步解析

异步解析有两个重载,异步解析的结构是通过迭代器返回。

  1. 重载1,正向解析,根据query来解析。
    template<
        typename ResolveHandler>
    void-or-deduced async_resolve(
        const query & q,
        ResolveHandler handler);
    void handler(
      const boost::system::error_code& error, 
      resolver::iterator iterator          
    );
  2. 重载2,逆向解析
    template<
        typename ResolveHandler>
    void-or-deduced async_resolve(
        const endpoint_type & e,
        ResolveHandler handler);

    异步回调函数的需要如下的结构

    void handler(
      const boost::system::error_code& error,
      resolver::iterator iterator    
    );

    来看下异步解析的例子

    #include "stdafx.h"
    #include <iostream>
    #include <boost/array.hpp>
    #include <boost/asio.hpp>
    
    using boost::asio::ip::tcp;
    void printErr(const boost::system::error_code& error, tcp::resolver::iterator iter)
    {
    	if(!error)
    	{
    		tcp::resolver::iterator end;
    		while (iter != end)
    		{
    			tcp::endpoint endpoint = *iter++;
    			std::cout << endpoint << std::endl;
    		}
    	}
    }
    
    int main()
    {
    	try
    	{
    		boost::asio::io_service io_service;
    		tcp::resolver resolver(io_service);
    		tcp::resolver::query query("www.unix8.net", "http");
    		resolver.async_resolve(query, 
    			boost::bind(&printErr,boost::asio::placeholders::error,
    			boost::asio::placeholders::iterator));
    		io_service.run();
    	}
    	catch (std::exception& e)
    	{
    		std::cerr << e.what() << std::endl;
    	}
    	system("pause");
    	return 0;
    }

    这里通过解析www.unix8.net的http服务,返回IP与端口。这里特别注意就是再bind中的参数。异步中要用boost::asio::placeholders::error与boost::asio::placeholders::iterator。

    namespace
    {
      boost::arg<1>& error
        = boost::asio::placeholders::detail::placeholder<1>::get();
      boost::arg<2>& bytes_transferred
        = boost::asio::placeholders::detail::placeholder<2>::get();
      boost::arg<2>& iterator
        = boost::asio::placeholders::detail::placeholder<2>::get();
      boost::arg<2>& signal_number
        = boost::asio::placeholders::detail::placeholder<2>::get();
    } // namespace

    以上为在异步中,和同步中不同的参数。下面的代码是逆向解析的片段。

		tcp::endpoint endpoint(boost::asio::ip::address::from_string("222.216.160.69"), 80);
		boost::system::error_code e;
		tcp::resolver::iterator iter = resolver.resolve(endpoint, e);

Boost::Asio::Error 与boost::asio::placeholders

2014年4月14日

一般而言我们创建用于接收error的类型大多声明如下:

boost::system::error_code error

我们用这个类型去接受在函数中产生的错误,如

socket.connect(endpoint, error);

如果连接失败,错误类型会保存到error中,比如连接主机失败可能会返回这样的错误:

boost::asio::error::host_not_found;

通过if (error)检测到error后,抛出异常

throw boost::system::system_error(error);

需要注意的是,我们的error被 转化成 system_error了,显示错误很简单了

std::cout << e.what();

然而还有一点在异步调用的时候产生的异常error 的传递是个问题,因为异步会立刻返回,局部变量是会被销毁的,boost::asio::placeholders::error,将会保存异常的状态,这样我们使用异步调用时如socket::async_write_some的时候不用自己创建boost::system::error_co error 了,直接使用boost::asio::placeholders::error作为参数即可,同理,我们sync_write_some需要返回读写数据的大小,令人开心的是boost::asio::placeholders::bytes_transferred直接作为参数就可以保存数据大小。

实例如下:

boost::asio::async_write(socket_, boost::asio::buffer(message_),
  boost::bind(&tcp_connection::handle_write, shared_from_this(),
  boost::asio::placeholders::error,
  boost::asio::placeholders::bytes_transferred));
}

参考手册上说的很明确, boost::asio::placeholders::error,boost::asio::placeholders::bytes_transferred就是为异步调用使用bind的时候设计的。当然了boost::system::error_co error还用有用的,同步调用的时候我们就用它作为参数如:

boost::system::error_code error;
size_t len = socket.read_some(boost::asio::buffer(buf), error);

同样在异步调用的回调handle中也用它作参数如

void handle_write(const boost::system::error_code& /*error*/,size_t /*bytes_transferred*/)
{
}

总结就是说异步就用 boost::asio::placeholders::error,boost::asio::placeholders::bytes_transferred,同步就用boost::system::error_code。

boost.asio中io_service分析

2014年4月14日

本文从源代码的角度进行来看看io_service

boost::asio::io_service io_service;

当使用同步方式编程时,没有io_service.run()调用;使用异步方式编程时,则有此调用,来看看这个函数实际上在做什么

std::size_t io_service::run()
{
  boost::system::error_code ec;
  std::size_t s = impl_.run(ec);
  boost::asio::detail::throw_error(ec);
  return s;
}

可以看出,实际上的主要工作已经委托给impl_,impl的本来意思就是执行,实施的意思。

既然这里使用了数据成员impl_,就不得不先来看看io_service类的组成为何方圣神,抛开一切函数,来看实际的数据成员,

#if defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__)
  detail::winsock_init<> init_;
#elif defined(__sun) || defined(__QNX__) || defined(__hpux) || defined(_AIX) \
  || defined(__osf__)
  detail::signal_init<> init_;
#endif

  // The service registry.
  boost::asio::detail::service_registry* service_registry_;

  // The implementation.
  impl_type& impl_;

其中的impl_实际上为

typedef detail::io_service_impl impl_type;

既然调用的是impl_.run(),不得不看这个run函数在干什么,在看run函数之前,得来看看impl_这个类型?

#if defined(BOOST_ASIO_HAS_IOCP)
  typedef class win_iocp_io_service io_service_impl;
  class win_iocp_overlapped_ptr;
#else
  typedef class task_io_service io_service_impl;
#endif

从这里看出,impl_在windows下使用的是windows IOCP模型,即win_iocp_io_service,在Linux下估计是使用的是EPOLL模型,但是没有细看,不知道是不是,反正这里命名为task_io_service。

class win_iocp_io_service
  : public boost::asio::detail::service_base<win_iocp_io_service>

class task_io_service
  : public boost::asio::detail::service_base<task_io_service>

在win_iocp_io_service类里可以看见调用的是windows系统提供的SDK里的文件。

这说明asio在windows里使用win_iocp_io_service来处理任务,因为io_service.run实际上调用的是win_iocp_io_service.run(),所以来看看该函数。

size_t win_iocp_io_service::run(boost::system::error_code& ec)
{
  if (::InterlockedExchangeAdd(&outstanding_work_, 0) == 0)
  {
    stop();
    ec = boost::system::error_code();
    return 0;
  }

  win_iocp_thread_info this_thread;
  thread_call_stack::context ctx(this, this_thread);

  size_t n = 0;
  while (do_one(true, ec))
    if (n != (std::numeric_limits<size_t>::max)())
      ++n;
  return n;
}

到这里可以看出,最终交给了以下函数来处理:

size_t win_iocp_io_service::do_one(bool block, boost::system::error_code& ec)

从这个函数的详细实现里可以看出,大概意思就是从队列中取操作来执行。

综上可以得出,io_service实际上就是利用iocp模型从队列中获取操作来执行。

 

io_servie 任务队列中的任务由io_service类的post函数提供

Post向队列中投递任务,然后激活空闲线程执行任务。其实现流程如下:

1. Post接收handler作为参数,实际上是个仿函数,通过此仿函数构造出completion_handler对象,completion_handler继承自operation。然后调用post_immediate_completion。

 typename op::ptr p = { boost::asio::detail::addressof(handler),
    boost_asio_handler_alloc_helpers::allocate(
      sizeof(op), handler), 0 };
  p.p = new (p.v) op(handler);

  BOOST_ASIO_HANDLER_CREATION((p.p, "io_service", this, "post"));

  post_immediate_completion(p.p, false);

2.post_immediate_completion首先将outstanding_work_增加,然后调用post_deferred_completion。

  void post_immediate_completion(win_iocp_operation* op, bool)
  {
    work_started();
    post_deferred_completion(op);
  }

3.post_deferred_completion首先加锁将任务入列,然后调用wake_one_thread_and_unlock

void win_iocp_io_service::post_deferred_completion(win_iocp_operation* op)
{
  // Flag the operation as ready.
  op->ready_ = 1;

  // Enqueue the operation on the I/O completion port.
  if (!::PostQueuedCompletionStatus(iocp_.handle, 0, 0, op))
  {
    // Out of resources. Put on completed queue instead.
    mutex::scoped_lock lock(dispatch_mutex_);
    completed_ops_.push(op);
    ::InterlockedExchange(&dispatch_required_, 1);
  }
}

4.wake_one_thread_and_unlock尝试唤醒当前空闲的线程,其实现中特别之处在于,若没有空闲线程,但是有线程在执行task->run,先阻塞。

5.  first_idle_thread_维护了所有当前空闲线程,每次唤醒时只唤醒空闲线程的第一个。

boost::bind应用分析

2014年4月9日

本文主要从应用角度讨论boost::bind,源码级别的分析以后有空再写写,这里不得不称赞boost::bind比起C++标准库的bind1st,bind2nd来说,支持的参数更多,使用也更简单,另外我觉得最好还是对泛型模板编程有一定了解,对C++标准库实现有了解。

1.自由函数,bind参数个数为自由函数参数个数+1

void func_void_int_int(int a, int b)
{
	cout << "自由函数func_void_int_int " <<a<<"   "<<b<< endl;
}

该自由函数返回为void,参数分别为int,int,使用bind也很简单。

	boost::bind(&func_void_int_int,3,4)();
	boost::bind(&func_void_int_int,3,_1)(4);
	boost::bind(&func_void_int_int,_1,_2)(5,6);
	boost::bind(&func_void_int_int,_1,_1)(7);

其中用了_1,_2占位符,而且还可以使用延迟指定参数。

2.函数对象(仿函数),bind参数个数为operator参数个数+1

class FuncObject_void_int
{
public:
	void operator()(int x)
	{
		cout<<"函数对象FuncObject_void_int "<<x<<endl;
	}
};

这个仿函数返回值为void,参数为int,在使用bind时,需要指定模板参数。

boost::bind<void>(FuncObject_void_int(), 4)();
boost::bind<void>(FuncObject_void_int(), _1)(4);

这里需要指定仿函数的返回值作为bind的模板形参,因为函数的参数都可以根据传进来的值进行推断,返回值则推断不了。

再来看看其他返回值情况

class FuncObject_int_int
{
public:
	int operator()(int x)
	{
		cout<<"函数对象FuncObject_int_int "<<x<<endl;
		return x;
	}
};
int answer = boost::bind<int>(FuncObject_int_int(), 10)();

3.继承自标准库的仿函数,bind参数个数为operator参数个数+1

class FuncObjectDerived_void_int : public unary_function<int, void>
{
public:
	void operator()(int x)
	{
		cout<<"函数对象FuncObjectDerived_void_int "<<x<<endl;
	}
};
class FuncObjectDerived_int_int_int : public  binary_function<int, int, int>
{
public:
	int operator()(int x,int y)
	{
		cout<<"函数对象FuncObjectDerived_int_int "<<x<<" "<<y<<endl;
		return x + y;
	}
};

这里使用继承了标准库的unary_function与binary_function,如果不知道这两个是干什么的,最好还是去看看文末尾给出的一些书籍。

当使用这两个函数对象时,不再需要模板参数了,因为通过他们的父类就可以获得

	boost::bind(FuncObjectDerived_void_int(), 11)();
	answer = boost::bind(FuncObjectDerived_int_int_int(), _1, _2)(13, 14);

4.类的非静态成员函数,bind参数个数为成员函数参数个数+2

第一个参数为成员函数指针,第二个参数表明绑定到this的对象,其他为参数

class ClassTest
{
public:
	ClassTest(int _a):a(_a){}
	void printA(int x)
	{
		cout<<"输出A x "<<a<<"  "<<x<<endl;
	}
	int getAB(int b)
	{
		cout<<"获得AB "<<a*b<<endl;
		return a;
	}
private:
	int a;
};

使用规则如下,这个时候就体现出了boost的强大特性,以不变应万变:

	ClassTest myClassTest(88);
	boost::bind(&ClassTest::printA, myClassTest,_1)(77);

	ClassTest* pClassTest = &myClassTest;
	boost::bind(&ClassTest::printA, pClassTest,_1)(88);

	boost::shared_ptr<ClassTest> spClassTest(new ClassTest(88));
	boost::bind(&ClassTest::printA, spClassTest, _1)(99);

5.参考文献

<超越C++标准库 boost库导论>

<STL源码剖析>

<C++标准程序库>

<泛型与模板编程>

源码:testBootst