最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

C++學(xué)習(xí)之智能指針中的unique_ptr與shared_ptr

 更新時間:2023年05月21日 14:43:44   作者:會玩code  
吃獨食的unique_ptr與樂于分享的shared_ptr是C++中常見的兩個智能指針,本文主要為大家介紹了這兩個指針的使用以及智能指針使用的原因,希望對大家有所幫助

為什么需要智能指針

在上一講《01 C++如何進行內(nèi)存資源管理》中,提到了對于堆上的內(nèi)存資源,需要我們手動分配和釋放。管理這些資源是個技術(shù)活,一不小心,就會導(dǎo)致內(nèi)存泄漏。

我們再給兩段代碼,切身體驗下原生指針管理內(nèi)存的噩夢。

void foo(int n) {
    int* ptr = new int(42);
    ...
    if (n > 5) {
	    return;
    }
    ...
    delete ptr;
}
void other_fn(int* ptr) {
	...
};
void bar() {
    int* ptr = new int(42);
    other_fn(ptr);
    // ptr == ?
}

foo函數(shù)中,如果入?yún)?code>n > 5, 則會導(dǎo)致指針ptr的內(nèi)存未被正確釋放,從而導(dǎo)致內(nèi)存泄漏。

bar函數(shù)中,我們將指針ptr傳遞給了另外一個函數(shù)other_fn,我們無法確定other_fn有沒有釋放ptr內(nèi)存,如果被釋放了,那ptr將成為一個懸空指針,bar在后續(xù)還繼續(xù)訪問它,會引發(fā)未定義行為,可能導(dǎo)致程序崩潰。

上面由于原生指針使用不當導(dǎo)致的內(nèi)存泄漏、懸空指針問題都可以通過智能指針來輕松避免。

C++智能指針是一種用于管理動態(tài)分配內(nèi)存的指針類?;赗AII設(shè)計理念,通過封裝原生指針實現(xiàn)的??梢栽谫Y源(原生指針對應(yīng)的對象)生命周期結(jié)束時自動釋放內(nèi)存。

C++標準庫中,提供了兩種最常見的智能指針類型,分別是std::unique_ptr 和 std::shared_ptr。
接下來我們分別詳細展開介紹。

吃獨食的unique_ptr

std::unique_ptr 是 C++11 引入的智能指針,用于管理動態(tài)分配的內(nèi)存。每個 std::unique_ptr 實例都擁有對其所包含對象的唯一所有權(quán),并在其生命周期結(jié)束時自動釋放對象。

創(chuàng)建unique_ptr對象

我們可以std::unique_ptr的構(gòu)造函數(shù)或std::make_unique函數(shù)(C++14支持)來創(chuàng)建一個unique_ptr對象,在超出作用域時,會自動釋放所管理的對象內(nèi)存。示例代碼如下:

#include <memory>
#include <iostream>
class MyClass {
public:
    MyClass() {
        std::cout << "MyClass constructed" << std::endl;
    }
    ~MyClass() {
        std::cout << "MyClass destroyed" << std::endl;
    }
};
int main() {
	std::unique_ptr<MyClass> ptr1(new MyClass);
	// C++14開始支持std::make_unique
    std::unique_ptr<int> ptr2 = std::make_unique<int>(10);
    return 0;
}

代碼輸出:

MyClass constructed
MyClass destroyed

訪問所管理的對象

我們可以像使用原生指針的方式一樣,訪問unique_ptr所指向的對象。也可以通過get函數(shù)獲取到原生指針。

MyClass* naked_ptr = ptr1.get();
std::cout << *ptr2 << std::endl; // 輸出 10

釋放/重置所管理的對象

使用reset函數(shù)可以釋放unique_ptr所管理的對象,并將其指針重置為nullptr或指定的新指針。reset`大概實現(xiàn)原理如下

template<class T> 
void unique_ptr<T>::reset(pointer ptr = pointer()) noexcept { 
	// 釋放指針指向的對象
	delete ptr_; 
	// 重置指針
	ptr_ = ptr;
}

該函數(shù)主要完成兩件事:

  • 釋放 std::unique_ptr 所管理的對象,以避免內(nèi)存泄漏。
  • 將 std::unique_ptr 重置為nullptr或管理另一個對象。

code show time:

#include <iostream>
#include <memory>
class MyClass {
public:
    MyClass() {
        std::cout << "MyClass constructed" << std::endl;
    }
    ~MyClass() {
        std::cout << "MyClass destroyed" << std::endl;
    }
};
int main() {
    // 創(chuàng)建一個 std::unique_ptr 對象,指向一個 MyClass 對象
    std::unique_ptr<MyClass> ptr(new MyClass);
    // 調(diào)用 reset,將 std::unique_ptr 重置為管理另一個 MyClass 對象
    ptr.reset(new MyClass);
    return;
}

移動所有權(quán)

一個對象資源只能同時被一個unique_ptr管理。當嘗試把一個unique_ptr直接賦值給另外一個unique_ptr會編譯報錯。

#include <memory>
int main() {
    std::unique_ptr<int> p1 = std::make_unique<int>(42);
    std::unique_ptr<int> p2 = p1; // 編譯報錯
    return 0;
}

為了把一個 std::unique_ptr 對象的所有權(quán)移動到另一個對象中,我們必須配合std::move移動函數(shù)。

#include <memory>
#include <iostream>
int main() {
    std::unique_ptr<int> p1 = std::make_unique<int>(42);
    std::unique_ptr<int> p2 = std::move(p1); // ok
    std::cout << *p2 << std::endl; // 42
    std::cout << (p1.get() == nullptr) << std::endl; // true
    return 0;
}

這個例子中, 我們把p1通過std::move將其管理對象的所有權(quán)轉(zhuǎn)移給了p2, 此時p2接管了對象,而p1不再擁有管理對象的所有權(quán),即無法再操作到該對象了。

樂于分享的shared_ptr

shared_ptr是C++11提供的另外一種常見的智能指針,與unique_ptr獨占對象方式不同,shared_ptr是一種共享式智能指針,允許多個shared_ptr指針共同擁有同一個對象,采用引用計數(shù)的方式來管理對象的生命周期。當所有的 shared_ptr 對象都銷毀時,才會自動釋放所管理的對象。

創(chuàng)建shared_ptr對象

同樣的,C++也提供了std::shared_ptr構(gòu)造函數(shù)和std::make_shared函數(shù)來創(chuàng)建std::shared_ptr對象。

#include <memory>
int main() {
	std::shared_ptr<int> p1(new int(10));
	std::shared_ptr<int> p2 = std::make_shared<int>(20);
	return;
}

多個shared_ptr共享一個對象

可以通過賦值操作實現(xiàn)多個shared_ptr共享一個資源對象,例如

std::shared_ptr<int>p3 = p2;

shared_ptr采用引用計數(shù)的方式管理資源對象的生命周期,通過分配一個額外內(nèi)存當計數(shù)器。

當一個新的shared_ptr被創(chuàng)建時,它對應(yīng)的計數(shù)器被初始化為1。每當賦值給另外一個shared_ptr共享同一個對象時,計數(shù)器值會加1。當某個shared_ptr被銷毀時,計數(shù)值會減1,當計數(shù)值變?yōu)?時,說明沒有任何shared_ptr引用這個對象,會將對象進行回收。

C++提供了use_count函數(shù)來獲取std::shared_ptr所管理對象的引用計數(shù),例如

std::cout << "p1 use count: " << p1.use_count() << std::endl;

釋放/重置所管理的對象

可以使用reset函數(shù)來釋放/重置shared_ptr所管理的對象。大概實現(xiàn)原理如下(不考慮并發(fā)場景)

void reset(T* ptr = nullptr) {
	if (ref_count != nullptr) { 
		(*ref_count)--;
		if (*ref_count == 0) { 
			delete data; 
			delete ref_count; 
		} 
	} 
	data = ptr; 
	ref_count = (data == nullptr) ? nullptr : new size_t(1); 
}

data指針來存儲管理的資源,指針ref_count 來存儲計數(shù)器的值。

在 reset 方法中,需要減少計數(shù)器的值,如果計數(shù)器減少后為 0,則需要釋放管理的資源,如果減少后不為0,則不會釋放之前的資源對象。

如果reset指定了新的資源指針,則需要重新設(shè)置 data 和 ref_count,并將計數(shù)器初始化為 1。否則,將計數(shù)器指針置為nullptr

shared_ptr使用注意事項

避免循環(huán)引用

由于 shared_ptr 具有共享同一個資源對象的能力,因此容易出現(xiàn)循環(huán)引用的情況。例如:

struct Node { 
	std::shared_ptr<Node> next; 
};
int main() {
	std::shared_ptr<Node> node1(new Node);
	std::shared_ptr<Node> node2(new Node); 
	node1->next = node2; 
	node2->next = node1;
}

在上述代碼中,node1 和 node2 互相引用,在析構(gòu)時會發(fā)現(xiàn)計數(shù)器的值不為0,不會釋放所管理的對象,產(chǎn)生內(nèi)存泄漏。

為了避免循環(huán)引用,可以將其中一個指針改為 weak_ptr 類型。weak_ptr也是一種智能指針,通常配合shared_ptr一起使用。

weak_ptr是一種弱引用,不對所指向的對象進行計數(shù)引用,也就是說,不增加所指對象的引用計數(shù)。當所有的shared_ptr都析構(gòu)了,不再指向該資源時,該資源會被銷毀,同時對應(yīng)的所有weak_ptr都會變成nullptr,這時我們就可以利用expired()方法來判斷這個weak_ptr是否已經(jīng)失效。

我們可以通過weak_ptrlock()方法來獲得一個指向共享對象的shared_ptr。如果weak_ptr已經(jīng)失效,lock()方法將返回一個空的shared_ptr

下面是weak_ptr的基本使用示例:

#include <iostream>
#include <memory>
int main() {
    std::shared_ptr<int> sp = std::make_shared<int>(42);
    // 創(chuàng)建shared_ptr對應(yīng)的weak_ptr指針
    std::weak_ptr<int> wp(sp);
	// 通過lock創(chuàng)建一個對應(yīng)的shared_ptr
    if (auto p = wp.lock()) {
        std::cout << "shared_ptr value: " << *p << std::endl;
        std::cout << "shared_ptr use_count: " << p.use_count() << std::endl;
    } else {
        std::cout << "wp is expired" << std::endl;
    }
	// 釋放shared_ptr指向的資源,此時weak_ptr失效
    sp.reset();
    std::cout << "wp is expired: " <<  wp.expired() << std::endl;
    return 0;
}

代碼輸出如下

shared_ptr value: 42
shared_ptr use_count: 2
wp is expired: 1

回到shared_ptr的循環(huán)引用問題,利用weak_ptr不會增加shared_ptr的引用計數(shù)的特點,我們將Node.next的類型改為weak_ptr, 避免node1和node2互相循環(huán)引用。修改后代碼如下

```cpp
struct Node { 
	std::weak_ptr<Node> next; 
};
int main() {
	std::shared_ptr<Node> node1(new Node);
	std::shared_ptr<Node> node2(new Node); 
	node1->next = std::weak_ptr<Node>(node2); 
	node2->next = std::weak_ptr<Node>(node1); ;
}

避免裸指針與shared_ptr混用

先看看以下代碼

int* q = new int(9);
{
	std::shared_ptr<int> p(new int(10));
	...
	q = p.get();
}
std::cout << *q << std::endl;

get函數(shù)返回 std::shared_ptr 所持有的指針,但是不會增加引用計數(shù)。所以在shared_ptr析構(gòu)時,將該指針指向的對象給釋放掉了,導(dǎo)致指針q變成一個懸空指針。

避免一個原始指針初始化多個shared_ptr

int* p = new int(10);
std::shared_ptr<int> ptr1(p);
// error: 兩個shared_ptr指向同一個資源,會導(dǎo)致重復(fù)釋放
std::shared_ptr<int> ptr2(p);

總結(jié)

避免手動管理內(nèi)存帶來的繁瑣和容易出錯的問題。我們今天介紹了三種智能指針:unique_ptr、shared_ptrweak_ptr。
每種智能指針都有各自的使用場景。unique_ptr用于管理獨占式所有權(quán)的對象,它不能拷貝但可以移動,是最輕量級和最快的智能指針。shared_ptr用于管理多個對象共享所有權(quán)的情況,它可以拷貝和移動。weak_ptr則是用來解決shared_ptr循環(huán)引用的問題。

到此這篇關(guān)于C++學(xué)習(xí)之智能指針中的unique_ptr與shared_ptr的文章就介紹到這了,更多相關(guān)C++智能指針內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 詳解C++中vector的理解以及模擬實現(xiàn)

    詳解C++中vector的理解以及模擬實現(xiàn)

    vector是表示可變大小數(shù)組的序列容器。這篇文章主要為大家詳細介紹了vector的理解以及模擬實現(xiàn),文中的示例代碼講解詳細,感興趣的可以了解一下
    2023-03-03
  • C語言實現(xiàn)簡單的學(xué)生學(xué)籍管理系統(tǒng)

    C語言實現(xiàn)簡單的學(xué)生學(xué)籍管理系統(tǒng)

    這篇文章主要為大家詳細介紹了C語言實現(xiàn)簡單的學(xué)生學(xué)籍管理系統(tǒng),文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-07-07
  • C++中NULL與nullptr的區(qū)別小結(jié)

    C++中NULL與nullptr的區(qū)別小結(jié)

    本文介紹了C++編程中NULL與nullptr的區(qū)別,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2025-07-07
  • C語言實現(xiàn)單鏈表的基本功能詳解

    C語言實現(xiàn)單鏈表的基本功能詳解

    鏈表是一個結(jié)構(gòu)體實現(xiàn)的一種線性表,它只能從前往后,不可以從后往前,在實現(xiàn)單鏈表的操作時,需要用指針來操作。本文主要介紹了實現(xiàn)單鏈表的基本功能的代碼示例,具有一定價值,感興趣的同學(xué)可以學(xué)習(xí)一下
    2021-11-11
  • strings命令分析淺談Go和C++編譯時的一點小區(qū)別

    strings命令分析淺談Go和C++編譯時的一點小區(qū)別

    今天小編就為大家分享一篇關(guān)于strings命令分析淺談Go和C++編譯時的一點小區(qū)別,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧
    2019-04-04
  • Matlab實現(xiàn)遺傳算法的示例詳解

    Matlab實現(xiàn)遺傳算法的示例詳解

    這篇文章主要為大家詳細介紹了什么是遺傳算法,以及如何利用Matlab從零開始自己寫一個遺傳算法函數(shù),文中的代碼對我們學(xué)習(xí)有一定幫助,需要的可以參考一下
    2022-03-03
  • c++?虛繼承的實現(xiàn)示例

    c++?虛繼承的實現(xiàn)示例

    本文主要介紹了c++?虛繼承的實現(xiàn)示例,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2025-06-06
  • 利用C語言實現(xiàn)任務(wù)調(diào)度的示例代碼

    利用C語言實現(xiàn)任務(wù)調(diào)度的示例代碼

    這篇文章主要為大家詳細介紹了如何利用純C語言實現(xiàn)任務(wù)調(diào)度(可用于STM32、C51等單片機),文中的示例代碼講解詳細,感興趣的小伙伴可以了解一下
    2023-04-04
  • opencv利用視頻的前n幀求平均圖像

    opencv利用視頻的前n幀求平均圖像

    這篇文章主要為大家詳細介紹了opencv利用視頻的前n幀求平均圖像,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2020-03-03
  • Cocos2d-x中CCEditBox文本輸入框的使用實例

    Cocos2d-x中CCEditBox文本輸入框的使用實例

    這篇文章主要介紹了Cocos2d-x中CCEditBox文本輸入框的使用實例,本文在代碼中用大量注釋講解了CCEditBox的使用方法,需要的朋友可以參考下
    2014-09-09

最新評論

东兰县| 丹棱县| 大厂| 磐安县| 大庆市| 泊头市| 临城县| 峨边| 惠东县| 德惠市| 陆良县| 诏安县| 威宁| 恩施市| 五寨县| 江安县| 沁水县| 炎陵县| 新乐市| 马尔康县| 铜山县| 姚安县| 南宫市| 曲阜市| 拉萨市| 河北区| 图们市| 东明县| 南乐县| 巴塘县| 闸北区| 莱阳市| 渝中区| 鹤庆县| 禄丰县| 富宁县| 聂拉木县| 中阳县| 紫云| 宽甸| 肃南|