对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. javaWeb RSA加密使用

      加密算法在各个网站运用很平常,今天整理代码的时候看到了我们项目中运用了RSA加密,就了解了一下. 先简单说一下RSA加密算法原理,RSA算法基于一个十分简单的数论事实:将两个大质数相乘十分容易,但 ...

  2. [转发]Gulp开发教程(翻译)

    Building With Gulp =================== 转载出处 原文地址 翻译出处 对网站资源进行优化,并使用不同浏览器测试并不是网站设计过程中最有意思的部分,但是这个过程中的 ...

  3. jquery取元素值

    var j = 1; if (rows.length > 0) { for (var i = 0; i < rows.length; i++) { var row = rows[i]; i ...

  4. VS2010安装MSDN

    VS2010正式版不再有单独的MSDN Library安装选项,以至于很多同学找不到本地的MSDN Library来用,其实VS2010的ISO安装光盘里已经包含有MSDN Library,只不过要手 ...

  5. 粗窥STARTUP.A51和INIT.A51

    也许大家曾经注意过使用Keil C51来编译链接生成目标代码之后,在我们的主程序之前有些代码不是我们写的,它们从哪里来的? Keil C51的\C51\LIB目录下有STARTUP.A51和INIT. ...

  6. Prime Palindrome Golf

    Prime Palindrome Golf Do you know how to play Prime Palindrome Golf? You are given a number and your ...

  7. 【HDU1514】Stars(树状数组)

    绝对大坑.千万记住树状数组0好下标位置是虚拟节点.详见大白书P195.其实肉眼看也能得出,在add(有的也叫update)的点修改操作中如果传入0就会死循环.最后TLE.所以下标+1解决问题.上代码! ...

  8. (转)linux下fork的运行机制

    转载http://www.cnblogs.com/leoo2sk/archive/2009/12/11/talk-about-fork-in-linux.html 给出如下C程序,在linux下使用g ...

  9. Linux2.6内核 -- 编码风格(3)

          9.typedef     内核开发者们强烈反对使用 typedef 语句.他们的理由是:     1> typedef 掩盖了数据的真实类型     2> 由于数据类型隐藏起 ...

  10. Linux内存点滴 用户进程内存空间

    Linux内存点滴 用户进程内存空间 经常使用top命令了解进程信息,其中包括内存方面的信息.命令top帮助文档是这么解释各个字段的. VIRT, Virtual Image (kb) RES, Re ...