欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

深入學(xué)習(xí)C++智能指針之shared_ptr與右值引用的方法

 更新時(shí)間:2021年07月13日 08:53:22   作者:進(jìn)擊的汪sir  
智能指針的核心實(shí)現(xiàn)技術(shù)是引用計(jì)數(shù),每使用它一次,內(nèi)部引用計(jì)數(shù)加1,每析構(gòu)一次內(nèi)部的引用計(jì)數(shù)減1,減為0時(shí),刪除所指向的堆內(nèi)存,今天通過本文給大家分享C++智能指針之shared_ptr與右值引用的方法,需要的朋友跟隨小編一起看看吧

1. 介紹

在 C++ 中沒有垃圾回收機(jī)制,必須自己釋放分配的內(nèi)存,否則就會(huì)造成內(nèi)存泄露。解決這個(gè)問題最有效的方法是使用智能指針(smart pointer)。智能指針是存儲(chǔ)指向動(dòng)態(tài)分配(堆)對(duì)象指針的類,用于生存期的控制,能夠確保在離開指針?biāo)谧饔糜驎r(shí),自動(dòng)地銷毀動(dòng)態(tài)分配的對(duì)象,防止內(nèi)存泄露。智能指針的核心實(shí)現(xiàn)技術(shù)是引用計(jì)數(shù),每使用它一次,內(nèi)部引用計(jì)數(shù)加1,每析構(gòu)一次內(nèi)部的引用計(jì)數(shù)減1,減為0時(shí),刪除所指向的堆內(nèi)存。

C++11 中提供了三種智能指針,使用這些智能指針時(shí)需要引用頭文件

  • std::shared_ptr:共享的智能指針
  • std::unique_ptr:獨(dú)占的智能指針
  • std::weak_ptr:弱引用的智能指針,它不共享指針,不能操作資源,是用來監(jiān)視 shared_ptr 的。

共享智能指針(shared_ptr)是指多個(gè)智能指針可以同時(shí)管理同一塊有效的內(nèi)存,共享智能指針 shared_ptr 是一個(gè)模板類,如果要進(jìn)行初始化有三種方式:通過構(gòu)造函數(shù)、std::make_shared 輔助函數(shù)以及 reset 方法。共享智能指針對(duì)象初始化完畢之后就指向了要管理的那塊堆內(nèi)存,如果想要查看當(dāng)前有多少個(gè)智能指針同時(shí)管理著這塊內(nèi)存可以使用共享智能指針提供的一個(gè)成員函數(shù) use_count

2. 初始化方法

2.1 通過構(gòu)造函數(shù)初始化

實(shí)例

// 使用智能指針管理一塊 int 型的堆內(nèi)存
shared_ptr<int> ptr1(new int(520));

2.2 通過拷貝和移動(dòng)構(gòu)造函數(shù)初始化

調(diào)用拷貝構(gòu)造函數(shù)

shared_ptr<int> ptr2(ptr1);

調(diào)用移動(dòng)構(gòu)造函數(shù)

std::shared_ptr<int> ptr5 = std::move(ptr2);

如果使用拷貝的方式初始化共享智能指針對(duì)象,這兩個(gè)對(duì)象會(huì)同時(shí)管理同一塊堆內(nèi)存,堆內(nèi)存對(duì)應(yīng)的引用計(jì)數(shù)也會(huì)增加;
如果使用移動(dòng)的方式初始智能指針對(duì)象,只是轉(zhuǎn)讓了內(nèi)存的所有權(quán),管理內(nèi)存的對(duì)象并不會(huì)增加,因此內(nèi)存的引用計(jì)數(shù)不會(huì)變化。

2.2.1 移動(dòng)構(gòu)造

關(guān)于移動(dòng)構(gòu)造,可能有些讀者不太明白

移動(dòng)構(gòu)造是C++11標(biāo)準(zhǔn)中提供的一種新的構(gòu)造方法。

在現(xiàn)實(shí)中有很多這樣的例子,我們將錢從一個(gè)賬號(hào)轉(zhuǎn)移到另一個(gè)賬號(hào),將手機(jī)SIM卡轉(zhuǎn)移到另一臺(tái)手機(jī),將文件從一個(gè)位置剪切到另一個(gè)位置……移動(dòng)構(gòu)造可以減少不必要的復(fù)制,帶來性能上的提升。

我們首先來看看move函數(shù)
首先看這樣一段代碼

#include <iostream>
#include <cstring>
#include <cstdlib>
#include <vector>

using namespace std;

int main()
{
	string st = "I love 進(jìn)擊的汪sir";
	vector<string> vc;
	vc.push_back(move(st));
	cout << vc[0] << endl;
	if (!st.empty())
		cout << st << endl;

	return 0;
}

輸出的結(jié)果為

再看這樣一段代碼

#include <iostream>
#include <cstring>
#include <cstdlib>
#include <vector>

using namespace std;

int main()
{
	string st = "I love xing";
	vector<string> vc;
	vc.push_back(st);
	cout << vc[0] << endl;
	if (!st.empty())
		cout << st << endl;

	return 0;
}

其結(jié)果為

這兩段代碼唯一的不同是調(diào)用vc.push_back()將字符串插入到容器中去時(shí),第一段代碼使用了move語句,而第二段代碼沒有使用move語句。輸出的結(jié)果差異也很明顯,第一段代碼中,原來的字符串st已經(jīng)為空,而第二段代碼中,原來的字符串st的內(nèi)容沒有變化。

先暫時(shí)記住這兩端代碼的輸出結(jié)果之間的差異。
我們回到移動(dòng)構(gòu)造函數(shù)上

有時(shí)候我們會(huì)遇到這樣一種情況,我們用對(duì)象a初始化對(duì)象b,后對(duì)象a我們就不在使用了,但是對(duì)象a的空間還在呀(在析構(gòu)之前),既然拷貝構(gòu)造函數(shù),實(shí)際上就是把a(bǔ)對(duì)象的內(nèi)容復(fù)制一份到b中,那么為什么我們不能直接使用a的空間呢?這樣就避免了新的空間的分配,大大降低了構(gòu)造的成本。這就是移動(dòng)構(gòu)造函數(shù)設(shè)計(jì)的初衷。

通俗一點(diǎn)的解釋就是,拷貝構(gòu)造函數(shù)中,對(duì)于指針,我們一定要采用深層復(fù)制,而移動(dòng)構(gòu)造函數(shù)中,對(duì)于指針,我們采用淺層復(fù)制。

所以在上面的例子中,如果調(diào)用移動(dòng)構(gòu)造函數(shù)來初始化智能指針,引用計(jì)數(shù)是不會(huì)增加的,而move函數(shù)實(shí)際上是返回的右值引用

2.2.2 右值引用

上面我們講到了右值引用,這里就來擴(kuò)展一下右值引用是啥
首先得分清楚,什么是右值,什么是左值

  • lvalue 是 loactor value 的縮寫,rvalue 是 read value 的縮寫
  • 左值是指存儲(chǔ)在內(nèi)存中、有明確存儲(chǔ)地址(可取地址)的數(shù)據(jù);
  • 右值是指可以提供數(shù)據(jù)值的數(shù)據(jù)(不可取地址);

通過描述可以看出,區(qū)分左值與右值的便捷方法是:可以對(duì)表達(dá)式取地址(&)就是左值,否則為右值 。所有有名字的變量或?qū)ο蠖际亲笾?,而右值是匿名的?/p>

C++11 中右值可以分為兩種:一個(gè)是將亡值( xvalue, expiring value),另一個(gè)則是純右值( prvalue, PureRvalue):

  • 純右值:非引用返回的臨時(shí)變量、運(yùn)算表達(dá)式產(chǎn)生的臨時(shí)變量、原始字面量和 lambda 表達(dá)式等
  • 將亡值:與右值引用相關(guān)的表達(dá)式,比如,T&& 類型函數(shù)的返回值、 std::move 的返回值等。

右值引用就是對(duì)一個(gè)右值進(jìn)行引用的類型。因?yàn)橛抑凳悄涿?,所以我們只能通過引用的方式找到它。無論聲明左值引用還是右值引用都必須立即進(jìn)行初始化,因?yàn)橐妙愋捅旧聿⒉粨碛兴壎▽?duì)象的內(nèi)存,只是該對(duì)象的一個(gè)別名。通過右值引用的聲明,該右值又“重獲新生”,其生命周期與右值引用類型變量的生命周期一樣,只要該變量還活著,該右值臨時(shí)量將會(huì)一直存活下去。

右值通過&&來引用

例如:

  • int&& value = 520; 里面 520 是純右值,value 是對(duì)字面量 520 這個(gè)右值的引用。
  • int &&a2 = a1; 中 a1 雖然寫在了 = 右邊,但是它仍然是一個(gè)左值,使用左值初始化一個(gè)右值引用類型是不合法的。
  • const Test& t = getObj() 這句代碼的語法是正確的,常量左值引用是一個(gè)萬能引用類型,它可以接受左值、右值、常量左值和常量右值。

2.3 通過 std::make_shared 初始化

通過 C++ 提供的 std::make_shared() 就可以完成內(nèi)存對(duì)象的創(chuàng)建并將其初始化給智能指針,函數(shù)原型如下:

template< class T, class... Args >
shared_ptr<T> make_shared( Args&&... args );

實(shí)例
使用智能指針管理一塊 int 型的堆內(nèi)存, 內(nèi)部引用計(jì)數(shù)為 1

shared_ptr<int> ptr1 = make_shared<int>(520);

注意
使用 std::make_shared() 模板函數(shù)可以完成內(nèi)存地址的創(chuàng)建,并將最終得到的內(nèi)存地址傳遞給共享智能指針對(duì)象管理。如果申請(qǐng)的內(nèi)存是普通類型,通過函數(shù)的()可完成地址的初始化,如果要?jiǎng)?chuàng)建一個(gè)類對(duì)象,函數(shù)的()內(nèi)部需要指定構(gòu)造對(duì)象需要的參數(shù),也就是類構(gòu)造函數(shù)的參數(shù)。

2.4 通過 reset 方法初始化

共享智能指針類提供的 std::shared_ptr::reset 方法函數(shù)原型如下:

void reset() noexcept;

template< class Y >
void reset( Y* ptr );

template< class Y, class Deleter >
void reset( Y* ptr, Deleter d );

template< class Y, class Deleter, class Alloc >
void reset( Y* ptr, Deleter d, Alloc alloc );
  • ptr:指向要取得所有權(quán)的對(duì)象的指針
  • d:指向要取得所有權(quán)的對(duì)象的指針
  • aloc:內(nèi)部存儲(chǔ)所用的分配器

實(shí)例

shared_ptr<int> ptr5;
ptr5.reset(new int(250));

3. 獲取原始指針

對(duì)應(yīng)基礎(chǔ)數(shù)據(jù)類型來說,通過操作智能指針和操作智能指針管理的內(nèi)存效果是一樣的,可以直接完成數(shù)據(jù)的讀寫。但是如果共享智能指針管理的是一個(gè)對(duì)象,那么就需要取出原始內(nèi)存的地址再操作,可以調(diào)用共享智能指針類提供的 get () 方法得到原始地址,其函數(shù)原型如下:

T* get() const noexcept;

實(shí)例

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

int main()
{
    int len = 128;
    shared_ptr<char> ptr(new char[len]);
    // 得到指針的原始地址
    char* add = ptr.get();
    memset(add, 0, len);
    strcpy(add, "博客:進(jìn)擊的汪sir");
    cout << "string: " << add << endl;
    
    shared_ptr<int> p(new int);
    *p = 100;
    cout << *p.get() << "  " << *p << endl;
    
    return 0;
}

4. 指定刪除器

當(dāng)智能指針管理的內(nèi)存對(duì)應(yīng)的引用計(jì)數(shù)變?yōu)?0 的時(shí)候,這塊內(nèi)存就會(huì)被智能指針析構(gòu)掉了。另外,我們?cè)诔跏蓟悄苤羔樀臅r(shí)候也可以自己指定刪除動(dòng)作,這個(gè)刪除操作對(duì)應(yīng)的函數(shù)被稱之為刪除器,這個(gè)刪除器函數(shù)本質(zhì)是一個(gè)回調(diào)函數(shù),我們只需要進(jìn)行實(shí)現(xiàn),其調(diào)用是由智能指針完成的。

實(shí)例

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

// 自定義刪除器函數(shù),釋放int型內(nèi)存
void deleteIntPtr(int* p)
{
    delete p;
    cout << "int 型內(nèi)存被釋放了...";
}

int main()
{
    shared_ptr<int> ptr(new int(250), deleteIntPtr);
    return 0;
}

刪除器函數(shù)也可以是 lambda 表達(dá)式!

5. 參考鏈接

https://subingwen.cn/cpp/shared_ptr/
https://www.cnblogs.com/qingergege/p/7607089.html

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

相關(guān)文章

  • c++回溯法解決1到9之間插入加減或空使運(yùn)算結(jié)果為100

    c++回溯法解決1到9之間插入加減或空使運(yùn)算結(jié)果為100

    編寫一個(gè)在1,2,…,9(順序不能變)數(shù)字之間插入+或-或什么都不插入,使得計(jì)算結(jié)果總是100的程序,并輸出所有的可能性。例如:1 + 2 + 34 – 5 + 67 – 8 + 9 = 100
    2021-10-10
  • 詳解Visual Studio 2019(VS2019) 基本操作

    詳解Visual Studio 2019(VS2019) 基本操作

    這篇文章主要介紹了詳解Visual Studio 2019(VS2019) 基本操作,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-03-03
  • C++?qsort函數(shù)排序與冒泡模擬實(shí)現(xiàn)流程詳解

    C++?qsort函數(shù)排序與冒泡模擬實(shí)現(xiàn)流程詳解

    qsort是一個(gè)庫函數(shù),基于快速排序算法實(shí)現(xiàn)的一個(gè)排序的函數(shù),下面這篇文章主要給大家介紹了關(guān)于C語言qsort()函數(shù)使用的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-10-10
  • C語言實(shí)現(xiàn)單鏈表的基本操作分享

    C語言實(shí)現(xiàn)單鏈表的基本操作分享

    單鏈表是一種鏈?zhǔn)酱嫒〉臄?shù)據(jù)結(jié)構(gòu),用一組地址任意的存儲(chǔ)單元存放線性表中的數(shù)據(jù)元素。本文將為大家介紹C語言中單鏈表的基本操作,需要的可以參考一下
    2022-10-10
  • C語言雙向鏈表的表示與實(shí)現(xiàn)實(shí)例詳解

    C語言雙向鏈表的表示與實(shí)現(xiàn)實(shí)例詳解

    這篇文章主要介紹了C語言雙向鏈表的表示與實(shí)現(xiàn),對(duì)于研究數(shù)據(jù)結(jié)構(gòu)域算法的朋友有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2014-07-07
  • c++自定義sort()函數(shù)的排序方法介紹

    c++自定義sort()函數(shù)的排序方法介紹

    這篇文章主要介紹了c++自定義sort()函數(shù)的排序方法介紹,文章通過圍繞主題展開詳細(xì)的內(nèi)容戒殺,具有一定的參考價(jià)值,需要的小伙伴可以參考一下
    2022-09-09
  • C語言實(shí)現(xiàn)猜數(shù)字

    C語言實(shí)現(xiàn)猜數(shù)字

    這篇文章主要為大家詳細(xì)介紹了C語言實(shí)現(xiàn)猜數(shù)字小游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-01-01
  • 基于Qt實(shí)現(xiàn)視頻播放器的制作

    基于Qt實(shí)現(xiàn)視頻播放器的制作

    本文主要為大家介紹了如何利用Qt中的qMediaPlayer和qvideowidget實(shí)現(xiàn)視頻文件(avi,mp4….)的播放,并且提供進(jìn)度顯示,還可以通過拖動(dòng)進(jìn)度條來變換播放位置,感興趣的可以嘗試一下
    2022-12-12
  • pcre函數(shù)詳細(xì)解析

    pcre函數(shù)詳細(xì)解析

    PCRE提供了19個(gè)接口函數(shù),為了簡(jiǎn)單介紹,使用PCRE內(nèi)帶的測(cè)試程序(pcretest.c)示例用法
    2013-09-09
  • C語言創(chuàng)建線程thread_create()的方法

    C語言創(chuàng)建線程thread_create()的方法

    這篇文章主要介紹了C語言創(chuàng)建線程thread_create()的方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-02-02

最新評(píng)論