对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 "); // } //}; // // //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日星期日