1. weak_ptr 介紹ios
std::weak_ptr 是一種智能指針,它對被 std::shared_ptr 管理的對象存在非擁有性("弱")引用。在訪問所引用的對象指針前必須先轉換爲 std::shared_ptr。 主要用來表示臨時全部權,當某個對象存在時才須要被訪問。轉換爲shared_ptr的過程等於對象的shared_ptr 的引用計數加一,所以若是你使用weak_ptr得到全部權的過程當中,原來的shared_ptr被銷燬,則該對象的生命期會被延長至這個臨時的 std::shared_ptr 被銷燬爲止。 weak_ptr還能夠避免 std::shared_ptr 的循環引用。git
std::weak_ptr簡單使用:(編譯系統:Linux centos 7.0 x86_64 編譯器:gcc 4.8.5 )github
#include <memory> #include <iostream> class foo { public: foo() { std::cout << "foo construct.." << std::endl; } void method() { std::cout << "welcome Test foo.." << std::endl; } ~foo() { std::cout << "foo destruct.." << std::endl; } }; int main() { // weak_ptr foo* foo2 = new foo(); // share_ptr 管理對象 std::shared_ptr<foo> shptr_foo2(foo2); // weak_ptr 弱引用 std::weak_ptr<foo> weak_foo2(shptr_foo2); // 若是要獲取數據指針,須要經過lock接口獲取 weak_foo2.lock()->method(); std::shared_ptr<foo> tmp = weak_foo2.lock(); // 咱們這邊有嘗試屢次獲取全部權(lock),看一下引用計數個數 std::cout << "shptr_foo2 RefCount: " << weak_foo2.lock().use_count() << std::endl; return 0; }
執行結果: centos
``` bash-4.2$ ./share_ptr foo construct.. welcome Test foo.. shptr_foo2 RefCount: 3 foo destruct..<p style="font-size: 15px; text-indent:2em; letter-spacing:1px; font-family: '微軟雅黑';">咱們能夠看到,weak_ptr屢次經過lock轉換成shared_ptr,得到shared_ptr後能夠成功調用管理對象的方法,這個過程當中<font color="#ff0000">引用計數是在增長的,所以若是原來的shared_ptr銷燬是不影響你這個臨時對象使用, 資源析構正常</font>。 下面是gnu STL 庫中最後調用lock函數會跑到的地方,引用計數 + 1 。(GNU STL 文件:shared_ptr_base.h) </p>  <p style="color: #AD5D0F;font-weight: bold;font-size: 20px; font-family: '微軟雅黑';">2. weak_ptr 實現和循環引用問題</p> ------ <p style="font-size: 15px; letter-spacing:1px; font-weight: bold; font-family: '微軟雅黑';">1. shared_ptr 循環引用問題</p> <p style="font-size: 15px; text-indent:2em; letter-spacing:1px; font-family: '微軟雅黑';">咱們首先看一下循環引用的問題,具體代碼以下: </p> 測試類:
class foo;
class Test
{
public:
Test()
{
std::cout << "construct.." << std::endl;
}bash
void method() { std::cout << "welcome Test.." << std::endl; } ~Test() { std::cout << "destruct.." << std::endl; }
public:
std::shared_ptr
};
class foo
{
public:
foo()
{
std::cout << "foo construct.." << std::endl;
}函數
void method() { std::cout << "welcome Test foo.." << std::endl; } ~foo() { std::cout << "foo destruct.." << std::endl; }
public:
std::shared_ptr
};
main函數:
int main()
{
// 循環引用 測試
Test* t2 = new Test();
foo* foo1 = new foo();測試
std::shared_ptr<Test> shptr_Test(t2); std::shared_ptr<foo> shptr_foo(foo1); std::cout << "shptr_Test RefCount: " << shptr_Test.use_count() << std::endl; std::cout << "shptr_foo RefCount: " << shptr_foo.use_count() << std::endl; shptr_Test->fooptr = shptr_foo; shptr_foo->testptr = shptr_Test; std::cout << "shptr_Test RefCount: " << shptr_Test.use_count() << std::endl; std::cout << "shptr_foo RefCount: " << shptr_foo.use_count() << std::endl; return 0;
}this
測試結果:
bash-4.2$ ./share_ptr
construct..
foo construct..
shptr_Test RefCount: 1
shptr_foo RefCount: 1
shptr_Test RefCount: 2
shptr_foo RefCount: 2
<p style="font-size: 15px; text-indent:2em; letter-spacing:1px; font-family: '微軟雅黑';">從打印結果能夠很明顯的看出,通過循環引用, 對象引用計數變成了2,而且執行完後,<font color="#ff0000">資源沒有釋放,沒有調用類的destruct(析構)函數</font>。</p> <p style="font-size: 15px; text-indent:2em; letter-spacing:1px; font-family: '微軟雅黑';">將類中的std::shared_ptr<foo> 換成 std::weak_ptr<foo>:</p>
class foo;
class Test
{
public:
Test()
{
std::cout << "construct.." << std::endl;
}
void method() { std::cout << "welcome Test.." << std::endl; } ~Test() { std::cout << "destruct.." << std::endl; }
public:
std::weak_ptr
};
class foo
{
public:
foo()
{
std::cout << "foo construct.." << std::endl;
}
void method() { std::cout << "welcome Test foo.." << std::endl; } ~foo() { std::cout << "foo destruct.." << std::endl; }
public:
std::weak_ptr
};
<p style="font-size: 15px; text-indent:2em; letter-spacing:1px; font-family: '微軟雅黑';">再看下weak_ptr的執行結果,能夠看到<font color="#ff0000">計數正常,資源成功釋放</font>:</p>
bash-4.2$ ./share_ptr
construct..
foo construct..
shptr_Test RefCount: 1
shptr_foo RefCount: 1
shptr_Test RefCount: 1
shptr_foo RefCount: 1
foo destruct..
destruct..
<p style="font-size: 15px; letter-spacing:1px; font-weight: bold; font-family: '微軟雅黑';">2. weak_ptr 實現</p> <p style="font-size: 15px; text-indent:2em; letter-spacing:1px; font-family: '微軟雅黑';">咱們這邊貼一下weak_ptr類的代碼:</p>
template
class weak_ptr
{
public:
template
friend class weak_ptr;
template <class S> friend class shared_ptr; constexpr weak_ptr() noexcept : m_iWeakRefCount(nullptr), m_ptr(nullptr) { } weak_ptr( const weak_ptr<T>& rhs ) noexcept : m_iWeakRefCount(rhs.m_iWeakRefCount) { m_ptr = rhs.lock().getPointer(); } weak_ptr( const shared_ptr<T>& rhs ) noexcept : m_iWeakRefCount(rhs.m_iRefCount), m_ptr(rhs.m_ptr) { } template <typename S> weak_ptr & operator=(const shared_ptr<S> & rhs) { m_ptr = dynamic_cast<T *>(rhs.m_ptr); m_iWeakRefCount = rhs.m_iRefCount; return *this; } template <typename S> weak_ptr & operator=(const weak_ptr<S> & rhs) { m_ptr = dynamic_cast<T *>(rhs.m_ptr); m_iWeakRefCount = rhs.m_iWeakRefCount; return *this; } weak_ptr& operator=( const weak_ptr& rhs ) noexcept { m_iWeakRefCount = rhs.m_iWeakRefCount; m_ptr = rhs.m_ptr; return *this; } weak_ptr& operator=( const shared_ptr<T>& rhs ) noexcept { m_iWeakRefCount = rhs.m_iRefCount; m_ptr = rhs.m_ptr; return *this; } shared_ptr<T> lock() const noexcept { shared_ptr<T> tmp; if(m_iWeakRefCount && *m_iWeakRefCount > 0) { tmp.m_iRefCount = m_iWeakRefCount; tmp.m_ptr = m_ptr; if(tmp.m_iRefCount) { ++(*tmp.m_iRefCount); } } return tmp; } int use_count() { return *m_iWeakRefCount; } bool expired() const noexcept { return *m_iWeakRefCount == 0; } void Reset() { m_ptr = NULL; m_iWeakRefCount = NULL; }
private:
int * m_iWeakRefCount;
T* m_ptr;
};
<p style="font-size: 15px; text-indent:2em; letter-spacing:1px; font-family: '微軟雅黑';">主要注意的是lock函數,若是計數指針爲空,那麼會返回一個空的shared_ptr,而後就是<font color="#ff0000">不能重載operator*和operator-> 操做符</font>。</p> 主要參考: <a href="https://zh.cppreference.com/w/cpp/memory/weak_ptr" target="_red"><font color=#00ffff size=5>cppreference.com</font></a> 完整實現見:<a href="https://github.com/Yejy813/stl_implement/tree/master/smart_ptr" target="_red"><font color=#00ffff size=5>smart_ptr</font></a> <p style="font-size: 15px; letter-spacing:1px; font-weight: bold; font-family: '微軟雅黑';">3. enable_shared_from_this</p> <p style="font-size: 15px; text-indent:2em; letter-spacing:1px; font-family: '微軟雅黑';">這邊還有一個點也要介紹一下,那就是enable_shared_from_this,這個主要是爲了處理<font color="#ff0000">在shared_ptr管理的對象中要使用該對象的指針所引出的問題</font>。 咱們看下下面這個例子: </p>
class foo
{
public:
std::shared_ptr
{
// 若是類中要返回本身的指針怎麼辦?
return std::shared_ptr
}
~foo() { std::cout << "foo destruct .. " << std::endl; }
};
int main()
{
std::shared_ptr
bp1->getptr();
std::cout << "bp1.use_count() = " << bp1.use_count() << std::endl;
}
<p style="font-size: 15px; text-indent:2em; letter-spacing:1px; font-family: '微軟雅黑';"><font color="#ff0000">看下結果,釋放兩次</font>:</p>
ash-4.2$ ./share_ptr
foo destruct ..
bp1.use_count() = 1
foo destruct ..
<p style="font-size: 15px; text-indent:2em; letter-spacing:1px; font-family: '微軟雅黑';">其實咱們都不用測試,由於你若是<font color="#ff0000">直接使用該對象的this指針又拷貝給另外一個shared_ptr,那不就等於兩個沒有關係的shared_ptr管理同一個對象了嗎? 釋放的時候等於會調用兩次該對象的析構函數</font>。enable_shared_from_this就是用來解決這個問題的。看下代碼:</p>
class foo : public std::enable_shared_from_this
{
public:
std::shared_ptr
{
return shared_from_this();
}
~foo() { std::cout << "foo destruct .. " << std::endl; }
};
int main()
{
std::shared_ptr
bp1->getptr();
std::cout << "bp1.use_count() = " << bp1.use_count() << std::endl;
}
<p style="font-size: 15px; text-indent:2em; letter-spacing:1px; font-family: '微軟雅黑';">看下結果,成功釋放:</p>
bash-4.2$ ./share_ptr
bp1.use_count() = 1
foo destruct ..
<p style="font-size: 15px; text-indent:2em; letter-spacing:1px; font-weight: bold; font-family: '微軟雅黑';"><font color="#ff0000">總結一下,weak_ptr本質是以一種觀察者的形象存在,它能夠獲取到觀察主體的狀態,可是沒法獲取直接獲取到觀察主體,沒法直接對觀察主體修改,沒法釋放觀察主體的資源,你只能經過轉換成shared_ptr來作一些事情。 和觀察者模式很像,訂閱,獲得觀察主體狀態,在多線程環境下會比較管用! </font></p> <p style="font-size: 15px;text-indent:60em;letter-spacing:1px; font-family: '微軟雅黑';">2018年9月30日00:40:02</p>