智能指針之 weak_ptr

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>

![](https://img2018.cnblogs.com/blog/1285081/201809/1285081-20180929233137997-1131170390.png)

<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>

測試類:

include

include

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 fooptr;
};
多線程

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 testptr;
};
oop

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 fooptr;
};

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 testptr;
};

<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 getptr()
{
// 若是類中要返回本身的指針怎麼辦?
return std::shared_ptr (this);
}

~foo() 
{ 
    std::cout << "foo destruct .. " << std::endl; 
}

};

int main()
{
std::shared_ptr bp1(new foo());
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 getptr()
{
return shared_from_this();
}

~foo() 
{ 
    std::cout << "foo destruct .. " << std::endl; 
}

};

int main()
{
std::shared_ptr bp1(new foo());
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>
相關文章
相關標籤/搜索