对boost::shared_from_this的进一步封装
对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的进一步封装的更多相关文章
- 进一步封装highchart,打造自己的图表插件:jHighChart.js
Highcharts 是一个用纯JavaScript编写的一个图表库, 能够很简单便捷的在web网站或是web应用程序添加有交互性的图表.支持的图表类型有曲线图.区域图.柱状图.饼状图.散状点图和综合 ...
- 对redis客户端jedis2.8.0的进一步封装
jedis2.8.0的进一步封装: 1.序列化存储对象 2.结合spring,创建redis连接池 3.提供了基础的单个实体操作,有序list操作和一对多关系list的操作,对list提供了分页的封装 ...
- DAO设计模式实现数据库的增删改查(进一步封装JDBC工具类)
DAO设计模式实现数据库的增删改查(进一步封装JDBC工具类) 一.DAO模式简介 DAO即Data Access Object,数据访问接口.数据访问:故名思义就是与数据库打交道.夹在业务逻辑与数据 ...
- 【Android】19.3 ContentProvider及安卓进一步封装后的相关类
分类:C#.Android.VS2015: 创建日期:2016-03-08 一.简介 ContentProvider:内容提供程序. Android的ContentProvider与.NET框架的EF ...
- Http请求封装(对HttpClient类的进一步封装,使之调用更方便。另外,此类管理唯一的HttpClient对象,支持线程池调用,效率更高)
package com.ad.ssp.engine.common; import java.io.IOException; import java.util.ArrayList; import jav ...
- MySQL数据库学习笔记(十一)----DAO设计模式实现数据库的增删改查(进一步封装JDBC工具类)
[声明] 欢迎转载,但请保留文章原始出处→_→ 生命壹号:http://www.cnblogs.com/smyhvae/ 文章来源:http://www.cnblogs.com/smyhvae/p/4 ...
- [iOS基础控件 - 4.4] 进一步封装"APP列表”,初见MVC模式
A.从ViewController分离View 之前的代码中,View的数据加载逻辑放在了总的ViewController中,增加了耦合性,应该对控制器ViewController隐藏数据加载到Vie ...
- QSqlDatabase的进一步封装(多线程支持+更加简单的操作)——同时支持MySQL, SQL Server和Sqlite
开发背景: 1.直接用QSqlDatabase我觉得太麻烦了: 2.对于某些数据库,多个线程同时使用一个QSqlDatabase的时候会崩溃: 3.这段时间没什么干货放出来觉得浑身不舒服,就想写一个. ...
- lodash源码分析之缓存使用方式的进一步封装
在世界上所有的民族之中,支配着他们的喜怒选择的并不是天性,而是他们的观点. --卢梭<社会与契约论> 本文为读 lodash 源码的第九篇,后续文章会更新到这个仓库中,欢迎 star:po ...
随机推荐
- js的水仙花数的输出
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8" ...
- java下的第一个redis
Redis支持很多编程语言的客户端,有C.C#.C++.Clojure.Common Lisp.Erlang.Go.Lua.Objective-C.PHP.Ruby.Scala,甚至更时髦的Node. ...
- ESP8266固件烧录方法
今天拿到ESP8266的板子,第一步是进行烧录固件. 首先是使用官方自带的参考文档,进行操作.发现每次烧录均卡在等待同步上电. 之后发现是烧录方法错误. 正确的烧录方法: 先按下FLASH不放,再按烧 ...
- 记录android开发博客
1.一国外android开发博客,值得关注 https://blog.stylingandroid.com/page/2/ 2.一个app设计博客,很赞 http://androidniceties. ...
- 复习了下自定义style的使用
一.为什么要自定义style 这是样式与控件本身脱离的一种方式.style就像html中的css,只负责自定义样式.View控件在layout中就只负责声明自己就可以了. 就像这样: 首先在style ...
- 关闭并且禁用ECSHOP缓存
ECSHOP的缓存机制从一定程度上可以减少ECSHOP反复读取数据库的几率,从而一定程度上降低服务器负担,提高访问速度.但是启用缓存机制,对一些新手站长也有不利的地方.我就遇到很多新手站长经常问,我明 ...
- C语言日期时间标准库
用思维导图整理: 代码: #include <stdio.h> #include <time.h> #include <string.h> int main() { ...
- Python 的开发环境
建议在Windows 下开发,成本低廉,简单,效率高. 综合下:开发的程序,Python Django (Mysql,PostgreSQL) Nginx Redis ,这一组组合可以适应不同的平台, ...
- linux操作系下RAR的使用
============zip文件的操作================================== zip -r data.zip data 解释:将data文件夹压缩成了data.zip格 ...
- 【转】图文并茂 Ubuntu使用Thunderbird方法指南
原文网址:http://os.51cto.com/art/201101/243445.htm Ubuntu 是一个启动速度超快.界面友好.安全性好的操作系统,它适用于桌面电脑.笔记本电脑.服务器以及上 ...