对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. C++中string类的基本用法

    #include <iostream> #include <set> using namespace std; int main() { string line; getlin ...

  2. vector-2

    assign函数 语法: void assign( input_iterator start, input_iterator end ); void assign( size_type num, co ...

  3. IT编程培训,线上线下,孰优孰劣

    现在Java培训机构确实参差不齐,主要有在线培训和线下培训两大类: 1,虚拟和现实的区别:不论视觉,听觉,体验上在线教学都不如线下教学. 2,学费问 题:在线教学由于成本低,不受地域,教学设备限制一般 ...

  4. ecstore使用paypal支付显示不支持此支付

    问题描述: ecstore使用paypal支付,下单结算时显示不支持此支付. 问题和代码分析: 1.首先必须要保证默认货币是paypal支持的货币,paypal目前支付 ["supportC ...

  5. Ibatis 后台打印完整的sql语句

    http://blog.csdn.net/deng11342/article/details/9122015 http://www.blogjava.net/libin2722/archive/200 ...

  6. SQL Server聚合函数

    聚合函数对一组值计算后返回单个值.除了count(统计项数)函数以外,其他的聚合函数在计算式都会忽略空值(null).所有的聚合函数均为确定性函数.即任何时候使用一组相同的输入值调用聚合函数执行后的返 ...

  7. PowerShell因为在此系统中禁止执行脚本解决方法

    PowerShell因为在此系统中禁止执行脚本解决方法   在Powershell直接脚本时会出现: 无法加载文件 ******.ps1,因为在此系统中禁止执行脚本.有关详细信息,请参阅 " ...

  8. Android,监控ContentProvider的数据改变

    有时候应用中需要监听ContentProvider的改变并提供响应,这时候就要利用ContentObserver类了 不管是ContentProvider中实现的,insert,delete,upda ...

  9. bzoj2243-染色(动态树lct)

    解析:增加三个变量lc(最左边的颜色),rc(最右边的颜色),sum(连续相同颜色区间段数).然后就是区间合并的搞法.我就不详细解释了,估计你已经想到 如何做了. 代码 #include<cst ...

  10. 【转】多文件目录下makefile文件递归执行编译所有c文件

    首先说说本次嵌套执行makefile文件的目的:只需make根目录下的makefile文件,即可编译所有c文件,包括子目录下的. 意义:自动化编译行为,以后编译自己的c文件时可把这些makefile文 ...