对boost::shared_from_this的进一步封装

熟悉异步编程的同学可能会对boost::shared_from_this有所了解。我们在传入回调的时候,通常会想要其带上当前类对象的上下文,或者回调本身就是类成员函数,那这个工作自然非this指针莫属了,像这样:

void sock_sender::post_request_no_lock()
{
Request &req = requests_.front();
boost::asio::async_write(*sock_ptr_,
boost::asio::buffer(req.buf_ptr->get_content()),
boost::bind(&sock_sender::self_handler, this, _1, _2));
}

然而回调执行的时候并不一定对象还存在。为了确保对象的生命周期大于回调,我们可以使类继承自boost::enable_shared_from_this,然后回调的时候使用boost::bind传入shared_from_this()返回的智能指针。由于boost::bind保存的是参数的副本,bind构造的函数对象会一直持有一个当前类对象的智能指针而使得其引用计数不为0,这就确保了对象的生存周期大于回调中构造的函数对象的生命周期,像这样:

class sock_sender
: public boost::enable_shared_from_this<sock_sender>
{
//...
};
void sock_sender::post_request_no_lock()
{
Request &req = requests_.front();
boost::asio::async_write(*sock_ptr_,
boost::asio::buffer(req.buf_ptr->get_content()),
boost::bind(&sock_sender::self_handler, shared_from_this(), _1, _2));
}

我们知道,当类继承自boost::enable_shared_from_this后,类便不能再创建栈上对象了,必须new。然而,代码却并没有阻止我们创建栈上对象,使用这个类的人若不清楚这点,很可能就会搞错,导致运行时程序崩溃。

为了既能享受boost::enable_shared_from_this带来的便利,又能禁止栈上对象的创建,我创建了类shared_from_this_base,但是也不是很友好,勉强能用。

先上代码:

#pragma once
#include <boost/enable_shared_from_this.hpp>
#include <boost/smart_ptr/shared_ptr.hpp> //as we know, enable_shared_from_this makes class can use shared_from_this to get self shared pointer
//if you inherit from enable_shared_from_this, you should 'new' instance instead of 'T t'
//but 'T t' is not disabled, so someone may use it and then get fatal errors
//this base class is designed to disable 'T t' while we can use 'shared_from_this'
//
//static function 'create' is the same to boost::make_shared
//http://www.boost.org/doc/libs/1_59_0/libs/smart_ptr/make_shared.html
//if you need to pass a non-const reference to a constructor of T
//you may do so by wrapping the parameter in a call to boost::ref
//and I only support 6 parameters at most
//
//
//usage steps:
// 1. your class T must inherit from shared_from_this_base<T>
// 2. shared_from_this_base<T> must be a friend class to your class,
// because shared_from_this_base<T> will use your class's ctor to create instance.
// typically, first line of your class declaration is "friend class shared_from_this_base<T>;"
// 3. declare your class's ctor as private,
// in order to prevent instance creating by any other ways.
// 4. you must declare your class's dtor as public,
// because shared_ptr will delete instance when never needed.
// (I don't know how to make shared_ptr be a friend class.)
// 5. use T::create to create instance and get its shared pointer in your code,
// still use 'shared_from_this' in your class to get self shared pointer
//
//
//example:
//
//class TestClass : public shared_from_this_base<TestClass>
//{
// friend class shared_from_this_base<TestClass>;
//
//private:
// TestClass(int count)
// {
// }
//
//public:
// ~TestClass()
// {
// }
//
//public:
// void func()
// {
// printf_s("func\r\n");
// }
//};
//
//
//typedef TestClass::ptr_type TestClassPtr;
//
//
//
//int main()
//{
// //TestClass t(5); //illegal
// TestClassPtr p = TestClass::create(5);
// p->func();
// TestClassPtr p2 = p->shared_from_this();
//
// return 0;
//} template<class T>
class shared_from_this_base
: virtual public boost::enable_shared_from_this<T>
{
protected:
shared_from_this_base(){}
virtual ~shared_from_this_base(){} public:
typedef T self_type;
typedef boost::shared_ptr<self_type> ptr_type; //see class ctor
static ptr_type create()
{
return ptr_type(new self_type());
} //see class ctor
template<typename A1>
static ptr_type create(const A1 &a1)
{
return ptr_type(new self_type(a1));
} //see class ctor
template<typename A1, typename A2>
static ptr_type create(const A1 &a1, const A2 &a2)
{
return ptr_type(new self_type(a1, a2));
} //see class ctor
template<typename A1, typename A2, typename A3>
static ptr_type create(const A1 &a1, const A2 &a2, const A3 &a3)
{
return ptr_type(new self_type(a1, a2, a3));
} //see class ctor
template<typename A1, typename A2, typename A3, typename A4>
static ptr_type create(const A1 &a1, const A2 &a2, const A3 &a3, const A4 &a4)
{
return ptr_type(new self_type(a1, a2, a3, a4));
} //see class ctor
template<typename A1, typename A2, typename A3, typename A4, typename A5>
static ptr_type create(const A1 &a1, const A2 &a2, const A3 &a3, const A4 &a4, const A5 &a5)
{
return ptr_type(new self_type(a1, a2, a3, a4, a5));
} //see class ctor
template<typename A1, typename A2, typename A3, typename A4, typename A5, typename A6>
static ptr_type create(const A1 &a1, const A2 &a2, const A3 &a3, const A4 &a4, const A5 &a5, const A6 &a6)
{
return ptr_type(new self_type(a1, a2, a3, a4, a5, a6));
}
};

我把注释也一并贴上了,是为了使这个头文件显得完整一点(虽然咱英语不专业),下面不会提供源码地址。

现在可以把以前从boost::enable_shared_from_this继承的类改为从shared_from_this_base了,还是用shared_from_this()获得自身的智能指针。

我在注释里也说了,必须将子类的构造函数声明为私有的,否则就跟boost::enable_shared_from_this有一样的问题了。但是父类又要能够构造子类对象,所以父类必须是子类的友元。析构又是shared_ptr调用的,我又不知如何使shared_ptr成为友元,所以还是乖乖的把析构函数声明为公有的,这也没什么负面影响。

接下来,既然把构造私有化了,总要提供一种创建对象的方法吧,这就是create。

create就是new了一个对象返回包裹它的一个智能指针,这是唯一的创建对象的路子。

可以看到create有n个重载,还都有模板参数,这是借鉴了boost::make_shared的实现,对传递的构造函数的参数做了一次转发。

可以看到create的参数都是常量引用,那么如果构造函数接受的参数是非常量引用怎么办?make_shared给出了解决方案,在这里http://www.boost.org/doc/libs/1_59_0/libs/smart_ptr/make_shared.html:用boost::ref封装参数。

喜欢探究的同学可以再看下boost::ref的实现,还是挺简单的,就是重载了类型转换操作符。

最后一点,我用了虚继承。因为继承层次一多,难免会出现菱形继承的问题,在这里做一下预防。

还有最最后一点,里头我放了两个typedef,是为了用的时候少打几个字母,也看着统一一点。

by mkdym

2015年11月8日星期日

对boost::shared_from_this的进一步封装的更多相关文章

  1. 进一步封装highchart,打造自己的图表插件:jHighChart.js

    Highcharts 是一个用纯JavaScript编写的一个图表库, 能够很简单便捷的在web网站或是web应用程序添加有交互性的图表.支持的图表类型有曲线图.区域图.柱状图.饼状图.散状点图和综合 ...

  2. 对redis客户端jedis2.8.0的进一步封装

    jedis2.8.0的进一步封装: 1.序列化存储对象 2.结合spring,创建redis连接池 3.提供了基础的单个实体操作,有序list操作和一对多关系list的操作,对list提供了分页的封装 ...

  3. DAO设计模式实现数据库的增删改查(进一步封装JDBC工具类)

    DAO设计模式实现数据库的增删改查(进一步封装JDBC工具类) 一.DAO模式简介 DAO即Data Access Object,数据访问接口.数据访问:故名思义就是与数据库打交道.夹在业务逻辑与数据 ...

  4. 【Android】19.3 ContentProvider及安卓进一步封装后的相关类

    分类:C#.Android.VS2015: 创建日期:2016-03-08 一.简介 ContentProvider:内容提供程序. Android的ContentProvider与.NET框架的EF ...

  5. Http请求封装(对HttpClient类的进一步封装,使之调用更方便。另外,此类管理唯一的HttpClient对象,支持线程池调用,效率更高)

    package com.ad.ssp.engine.common; import java.io.IOException; import java.util.ArrayList; import jav ...

  6. MySQL数据库学习笔记(十一)----DAO设计模式实现数据库的增删改查(进一步封装JDBC工具类)

    [声明] 欢迎转载,但请保留文章原始出处→_→ 生命壹号:http://www.cnblogs.com/smyhvae/ 文章来源:http://www.cnblogs.com/smyhvae/p/4 ...

  7. [iOS基础控件 - 4.4] 进一步封装"APP列表”,初见MVC模式

    A.从ViewController分离View 之前的代码中,View的数据加载逻辑放在了总的ViewController中,增加了耦合性,应该对控制器ViewController隐藏数据加载到Vie ...

  8. QSqlDatabase的进一步封装(多线程支持+更加简单的操作)——同时支持MySQL, SQL Server和Sqlite

    开发背景: 1.直接用QSqlDatabase我觉得太麻烦了: 2.对于某些数据库,多个线程同时使用一个QSqlDatabase的时候会崩溃: 3.这段时间没什么干货放出来觉得浑身不舒服,就想写一个. ...

  9. lodash源码分析之缓存使用方式的进一步封装

    在世界上所有的民族之中,支配着他们的喜怒选择的并不是天性,而是他们的观点. --卢梭<社会与契约论> 本文为读 lodash 源码的第九篇,后续文章会更新到这个仓库中,欢迎 star:po ...

随机推荐

  1. 关于Ajax的技术组成与核心原理

    1.Ajax 特点: 局部刷新.提高用户的体验度,数据从服务器商加载 2.AJax的技术组成 不是新技术,而是之前技术的整合 Ajax: Asynchronous Javascript And Xml ...

  2. OpenGL ES 2.0 内置变量

    1. 顶点着色器中的内置变量 输出变量 gl_Position(vec4):顶点数据位置gl_PointSize(float):计算一个点的大小 2.片元着色器中的内置变量 输入变量gl_FragCo ...

  3. python字符串的encode和decode

    原文 decode的作用是将其他编码的字符串转换成unicode编码. str1.decode('gb2312') #表示将gb2312编码的字符串转换成unicode编码 encode的作用是将un ...

  4. [转]JS基础之undefined与null的区别

    在JavaScript开发中,被人问到:null与undefined到底有啥区别? 一时间不好回答,特别是undefined,因为这涉及到undefined的实现原理.于是,细想之后,写下本文,请各位 ...

  5. 图文-水平垂直居中兼容ie6+

    图文-水平垂直居中兼容ie6+ 具体代码: <!DOCTYPE html> <html> <head> <meta charset="utf-8&q ...

  6. maven项目依赖被改为文件夹时如何改回lib

    如图

  7. 答:我们公司的ASP.NET 笔试题,你觉得难度如何

    闲来无事,逛逛园子,发现有个面试题,觉得有意思.已自己的理解答来看看,不足之处,请多指教. 原文地址:http://www.cnblogs.com/leotsai/p/aspnet-tests-for ...

  8. 【转】嵌入式Linux学习笔记

    一  嵌入式系统定义: 应用于特定环境的硬件体系. 二  两样非常重要的能力: 1.  掌握各种新概念的能力 2.  调试的能力( 包括软件, 硬件 ) 三  需要的基础知识: 1.  操作系统理论基 ...

  9. hdu 六度分离

    http://acm.hdu.edu.cn/showproblem.php?pid=1869 #include <cstdio> #include <cstring> #inc ...

  10. Android开源中国客户端学习 (自定义View)左右滑动控件ScrollLayout

    左右滑动的控件我们使用的也是非常多了,但是基本上都是使用的viewpager 等 android基础的控件,那么我们有么有考虑过查看他的源码进行定制呢?当然,如果你自我感觉非常好的话可以自己定制一个, ...