熟悉異步編程的同學可能會對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日星期日