1. 程式人生 > 程式設計 >C++11智慧指標之weak_ptr詳解

C++11智慧指標之weak_ptr詳解

如題,我們今天要講的是 C++11 引入的三種智慧指標中的:weak_ptr。

在學習 weak_ptr 之前最好對 shared_ptr 有所瞭解。如果你還不知道 shared_ptr 是何物,可以看看另一篇文章:

【C++11新特性】 C++11智慧指標之shared_ptr

1、為什麼需要weak_ptr?

在正式介紹weak_ptr之前,我們先來回憶一下shared_ptr的一些知識。

我們知道shared_ptr是採用引用計數的智慧指標,多個shared_ptr例項可以指向同一個動態物件,並維護了一個共享的引用計數器。

對於引用計數法實現的計數,總是避免不了迴圈引用(或環形引用)的問題,shared_ptr也不例外。

我們先來看看下面這個例子:

#include <iostream>
#include <memory>
#include <vector>
using namespace std;

class ClassB;

class ClassA
{
public:
 ClassA() { cout << "ClassA Constructor..." << endl; }
 ~ClassA() { cout << "ClassA Destructor..." << endl; }
 shared_ptr<ClassB> pb; // 在A中引用B
};

class ClassB
{
public:
 ClassB() { cout << "ClassB Constructor..." << endl; }
 ~ClassB() { cout << "ClassB Destructor..." << endl; }
 shared_ptr<ClassA> pa; // 在B中引用A
};

int main() {
 shared_ptr<ClassA> spa = make_shared<ClassA>();
 shared_ptr<ClassB> spb = make_shared<ClassB>();
 spa->pb = spb;
 spb->pa = spa;
 // 函式結束,思考一下:spa和spb會釋放資源麼?
}

上面程式碼的輸出如下:

ClassA Constructor...
ClassB Constructor...
Program ended with exit code: 0

從上面程式碼中,ClassA和ClassB間存在著迴圈引用,從執行結果中我們可以看到:當main函式執行結束後,spa和spb管理的動態資源並沒有得到釋放,產生了記憶體洩露。

為了解決類似這樣的問題,C++11引入了weak_ptr,來打破這種迴圈引用。

2、weak_ptr是什麼?

weak_ptr 是為了配合 shared_ptr 而引入的一種智慧指標,它指向一個由 shared_ptr 管理的物件而不影響所指物件的生命週期,也就是將一個 weak_ptr 繫結到一個 shared_ptr 不會改變 shared_ptr 的引用計數。

不論是否有 weak_ptr 指向,一旦最後一個指向物件的 shared_ptr 被銷燬,物件就會被釋放。

從這個角度看,weak_ptr更像是shared_ptr的一個助手而不是智慧指標。

3、weak_ptr如何使用?

接下來,我們來看看weak_ptr的簡單用法。

3.1如何建立weak_ptr例項

當我們建立一個weak_ptr時,需要用一個 shared_ptr 例項來初始化 weak_ptr,由於是弱共享,weak_ptr 的建立並不會影響 shared_ptr 的引用計數值。

示例:

int main() {
 shared_ptr<int> sp(new int(5));
 cout << "建立前sp的引用計數:" << sp.use_count() << endl; // use_count = 1

 weak_ptr<int> wp(sp);
 cout << "建立後sp的引用計數:" << sp.use_count() << endl; // use_count = 1
}

3.2如何判斷weak_ptr指向物件是否存在

既然weak_ptr並不改變其所共享的shared_ptr例項的引用計數,那就可能存在weak_ptr指向的物件被釋放掉這種情況。

這時,我們就不能使用weak_ptr直接訪問物件。那麼我們如何判斷weak_ptr指向物件是否存在呢?

C++中提供了lock函式來實現該功能。

如果物件存在,lock()函式返回一個指向共享物件的shared_ptr,否則返回一個空shared_ptr。

示例:

class A
{
public:
 A() : a(3) { cout << "A Constructor..." << endl; }
 ~A() { cout << "A Destructor..." << endl; }

 int a;
};

int main() {
 shared_ptr<A> sp(new A());
 weak_ptr<A> wp(sp);
 //sp.reset();

 if (shared_ptr<A> pa = wp.lock())
 {
 cout << pa->a << endl;
 }
 else
 {
 cout << "wp指向物件為空" << endl;
 }
}

試試把sp.reset()這行的註釋去掉看看結果有什麼不同。

除此之外,weak_ptr還提供了expired()函式來判斷所指物件是否已經被銷燬。

示例:

class A
{
public:
 A() : a(3) { cout << "A Constructor..." << endl; }
 ~A() { cout << "A Destructor..." << endl; }

 int a;
};

int main() {
 shared_ptr<A> sp(new A());
 weak_ptr<A> wp(sp);
 sp.reset(); // 此時sp被銷燬
 cout << wp.expired() << endl; // true表示已被銷燬,否則為false
}

程式碼輸入如下:

A Constructor...
A Destructor...
1

3.3如何使用weak_ptr

weak_ptr並沒有過載 operator->和 operator *操作符,因此不可直接通過weak_ptr使用物件,典型的用法是呼叫其lock函式來獲得shared_ptr示例,進而訪問原始物件。

最後,我們來看看如何使用weak_ptr來改造最前面的程式碼,打破迴圈引用問題。

class ClassB;

class ClassA
{
public:
 ClassA() { cout << "ClassA Constructor..." << endl; }
 ~ClassA() { cout << "ClassA Destructor..." << endl; }
 weak_ptr<ClassB> pb; // 在A中引用B
};

class ClassB
{
public:
 ClassB() { cout << "ClassB Constructor..." << endl; }
 ~ClassB() { cout << "ClassB Destructor..." << endl; }
 weak_ptr<ClassA> pa; // 在B中引用A
};

int main() {
 shared_ptr<ClassA> spa = make_shared<ClassA>();
 shared_ptr<ClassB> spb = make_shared<ClassB>();
 spa->pb = spb;
 spb->pa = spa;
 // 函式結束,思考一下:spa和spb會釋放資源麼?
}

輸出結果如下:

ClassA Constructor...
ClassB Constructor...
ClassA Destructor...
ClassB Destructor...
Program ended with exit code: 0

從執行結果可以看到spa和spb指向的物件都得到釋放!

總結

到此這篇關於C++11智慧指標之weak_ptr詳解的文章就介紹到這了,更多相關C++11智慧指標 weak_ptr內容請搜尋我們以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援我們!