c++ boost enable_shared_from_this类的应用

介绍

    boost的enable_shared_from_this是一个模板类,它的使用是一个基于成员函数返回shared_ptr。有关shared_ptr的使用,请参考我的另一篇文章boost shared_ptr的使用

    boost::enable_shared_from_this主要就是shared_from_this这个函数,它的主要代码如下:

namespace boost
{

template<class T> class enable_shared_from_this
{
public:

    shared_ptr<T> shared_from_this();
    shared_ptr<T const> shared_from_this() const;
}

}

    返回的都是shared_ptr,只不过一个是const一个是非const,很好理解。

    此处参考boost官网的一个例子:

#include <boost/enable_shared_from_this.hpp>
#include <boost/shared_ptr.hpp>
#include <cassert>

class Y: public boost::enable_shared_from_this<Y>
{
public:

    boost::shared_ptr<Y> f()
    {
        return shared_from_this();
    }
};

int main()
{
    boost::shared_ptr<Y> p(new Y);
    boost::shared_ptr<Y> q = p->f();
    assert(p == q);
    assert(!(p < q || q < p)); // p and q must share ownership
}

    类Y继承自boost::enable_shared_from_this<Y>,很多初学者看到这里就感到很奇怪,怎么自己继承的地方模板参数还是自己,其实,稍微对泛型编程有点了解就知道,这里把Y当成模板参数,然后我们看看enable_shared_from_this这个模板类的实现,就会发现,Y参数只是用来shared_ptr的模板参数,意思就是说enable_shared_from_this的函数shared_from_this只是基于this指针构造了一个shared_ptr智能指针返回。想必现在大家都理解清楚了,一个类继承自boost::enable_shared_from_this,可以再自己类的内部用shared_from_this来引用自身。那么问题来了,如果不用new来定义对象会怎样呢?

    大家看看下面2段代码的区别:

class Y: public boost::enable_shared_from_this<Y>
{
public:

	boost::shared_ptr<Y> f()
	{
		return shared_from_this();
	}

	void test()
	{
		std::cout << "test" << std::endl;
	}
};

int main()
{
	Y y;
	y.test();
}

class Y: public boost::enable_shared_from_this<Y>
{
public:

	boost::shared_ptr<Y> f()
	{
		return shared_from_this();
	}

	void test()
	{
		std::cout << "test" << std::endl;
	}
};

int main()
{
	Y y;
	boost::shared_ptr<Y> pY = y.f();
	assert(nullptr != pY);
	pY->test();
}

    运行下,就可以知道结果,为什么会有这种结果呢,很简单,因为上面说得很清楚,enable_shared_from_this是把this指针包装成智能指针,那如果你是不是new出来的对象,而是在栈上分配的对象,你不引用还好,如果你一旦用了shared_from_this函数,那么,你自己想想,你这块内存到底是由系统给你释放,还是由智能指针给你释放呢?这必然造成冲突。

实践

    boost::enable_shared_from_this主要用于什么地方呢?我第一次见到它也是再boost.asio库中见到,也是一个很好的Proactor跨平台网络库。以下是boost官网的一个小例子:

//
// async_tcp_echo_server.cpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2014 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//

#include <cstdlib>
#include <iostream>
#include <boost/bind.hpp>
#include <boost/asio.hpp>

using boost::asio::ip::tcp;

class session
{
public:
  session(boost::asio::io_service& io_service)
    : socket_(io_service)
  {
  }

  tcp::socket& socket()
  {
    return socket_;
  }

  void start()
  {
    socket_.async_read_some(boost::asio::buffer(data_, max_length),
        boost::bind(&session::handle_read, this,
          boost::asio::placeholders::error,
          boost::asio::placeholders::bytes_transferred));
  }

private:
  void handle_read(const boost::system::error_code& error,
      size_t bytes_transferred)
  {
    if (!error)
    {
      boost::asio::async_write(socket_,
          boost::asio::buffer(data_, bytes_transferred),
          boost::bind(&session::handle_write, this,
            boost::asio::placeholders::error));
    }
    else
    {
      delete this;
    }
  }

  void handle_write(const boost::system::error_code& error)
  {
    if (!error)
    {
      socket_.async_read_some(boost::asio::buffer(data_, max_length),
          boost::bind(&session::handle_read, this,
            boost::asio::placeholders::error,
            boost::asio::placeholders::bytes_transferred));
    }
    else
    {
      delete this;
    }
  }

  tcp::socket socket_;
  enum { max_length = 1024 };
  char data_[max_length];
};

class server
{
public:
  server(boost::asio::io_service& io_service, short port)
    : io_service_(io_service),
      acceptor_(io_service, tcp::endpoint(tcp::v4(), port))
  {
    start_accept();
  }

private:
  void start_accept()
  {
    session* 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* new_session,
      const boost::system::error_code& error)
  {
    if (!error)
    {
      new_session->start();
    }
    else
    {
      delete new_session;
    }

    start_accept();
  }

  boost::asio::io_service& io_service_;
  tcp::acceptor acceptor_;
};

int main(int argc, char* argv[])
{
  try
  {
    if (argc != 2)
    {
      std::cerr << "Usage: async_tcp_echo_server <port>\n";
      return 1;
    }

    boost::asio::io_service io_service;

    using namespace std; // For atoi.
    server s(io_service, atoi(argv[1]));

    io_service.run();
  }
  catch (std::exception& e)
  {
    std::cerr << "Exception: " << e.what() << "\n";
  }

  return 0;
}

    对,没错,首先,我让大家看的是没有用enable_shared_from_this的例子,看到没有,看到地方出来了delete操作,这还只是一个很简单的echo服务,真正的产品是不可能有这么简单的,其中判断条件很多,那么,难道要在每个错误的地方都用delete释放内存吗?如果忘记了1,2个地方呢?那势必造成内存泄漏。所以,这里是一个利用enable_shared_from_this的好地方:

#include <cstdlib>
#include <iostream>
#include <boost/bind.hpp>
#include <boost/asio.hpp>
#include <boost/enable_shared_from_this.hpp>

using boost::asio::ip::tcp;

class session : public boost::enable_shared_from_this<session>
{
public:
	session(boost::asio::io_service& io_service)
		: socket_(io_service)
	{
	}

	tcp::socket& socket()
	{
		return socket_;
	}

	void start()
	{
		socket_.async_read_some(boost::asio::buffer(data_, max_length),
			boost::bind(&session::handle_read, shared_from_this(),
			boost::asio::placeholders::error,
			boost::asio::placeholders::bytes_transferred));
	}

private:
	void handle_read(const boost::system::error_code& error,
		size_t bytes_transferred)
	{
		if (!error)
		{
			boost::asio::async_write(socket_,
				boost::asio::buffer(data_, bytes_transferred),
				boost::bind(&session::handle_write, shared_from_this(),
				boost::asio::placeholders::error));
		}
	}

	void handle_write(const boost::system::error_code& error)
	{
		if (!error)
		{
			socket_.async_read_some(boost::asio::buffer(data_, max_length),
				boost::bind(&session::handle_read, shared_from_this(),
				boost::asio::placeholders::error,
				boost::asio::placeholders::bytes_transferred));
		}
	}

	tcp::socket socket_;
	enum { max_length = 1024 };
	char data_[max_length];
};

typedef boost::shared_ptr<session> sessionPtr;

class server
{
public:
	server(boost::asio::io_service& io_service, short port)
		: io_service_(io_service),
		acceptor_(io_service, tcp::endpoint(tcp::v4(), port))
	{
		start_accept();
	}

private:
	void start_accept()
	{
		sessionPtr 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(const sessionPtr& new_session,
		const boost::system::error_code& error)
	{
		if (!error)
		{
			new_session->start();
		}

		start_accept();
	}

	boost::asio::io_service& io_service_;
	tcp::acceptor acceptor_;
};

int main(int argc, char* argv[])
{
	try
	{
		if (argc != 2)
		{
			std::cerr << "Usage: async_tcp_echo_server <port>\n";
			return 1;
		}

		boost::asio::io_service io_service;

		using namespace std; // For atoi.
		server s(io_service, atoi(argv[1]));

		io_service.run();
	}
	catch (std::exception& e)
	{
		std::cerr << "Exception: " << e.what() << "\n";
	}

	return 0;
}

    现在看看,一个小小的改变,是不是让代码看起来优雅多了呢?而且根本不用去担心内存泄漏的问题。所有的内存问题都交由shared_ptr去管理了。这里主要阐述下以上的原理:
        1.首先继承自enable_shared_from_this
        2.然后用shared_ptr去存储new出来的对象
        3.接着调用start函数去接收数据,在这里,bind函数的参数是shared_from_this,所以,bind会持有这个智能指针,session不会被释放掉,直到bind回调handle_read
        4.在handle_read回调中,如果没有错误,则把shared_from_this传递给bind继续持有,否则,释放内存
        5.在handle_write回调中对智能指针的处理结果同4.

    在整个过程中,你完全不必去担心你new出来的对象什么时候释放或者是由谁来释放,完全由boost给你一手承包,让你专注在业务处理中。好了,今天的enable_shared_from_this就介绍到这里了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Z小偉

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值