亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

一篇文章帶你了解C++智能指針詳解

 更新時(shí)間:2021年08月13日 11:39:14   作者:z向前  
這篇文章主要介紹了c++ 智能指針基礎(chǔ)的相關(guān)資料,幫助大家更好的理解和學(xué)習(xí)使用c++,感興趣的朋友可以了解下,希望能給你帶來(lái)幫助

為什么要有智能指針?

因?yàn)槠胀ǖ闹羔槾嬖谝韵聨讉€(gè)問(wèn)題:

  • 資源泄露
  • 野指針
    •  未初始化
    • 多個(gè)指針指向同一塊內(nèi)存,某個(gè)指針將內(nèi)存釋放,別的指針不知道
  • 異常安全問(wèn)題
  • 如果在 malloc和free 或者 new和delete 之間如果存在拋異常,那么也會(huì)導(dǎo)致內(nèi)存泄漏。

資源泄漏示例代碼:

int main(){
	int *p = new int;
	*p = 1;
	p = new int; // 未釋放之前申請(qǐng)的資源,導(dǎo)致內(nèi)存泄漏 
	delete p;
	
	return 0;
}

野指針示例代碼:

int main(){
	int *p1 = new int;
	int *p2 = p1;
	delete p1; 
	*p2 = 1; // 申請(qǐng)的內(nèi)存已經(jīng)被釋放掉了,  
	
	return 0;
}
int main(){
	int *p;
	*p = 1; // 程序直接報(bào)錯(cuò), 使用了未初始化的變量
	return 0;
}

解決方法:智能指針

智能指針的使用及原理

  • 具有RALL 特性
  • 重載了 operator* 和 operator ->,使其具有了指針一樣的行為

RALL

RALL(Resource Acquistion Is Initialization)是一種利用對(duì)象生命周期來(lái)控制程序資源(如內(nèi)存,文件句柄,網(wǎng)絡(luò)連接,互斥量等)的簡(jiǎn)單技術(shù)。

在對(duì)象構(gòu)造時(shí)獲取資源,接著控制對(duì)資源的訪(fǎng)問(wèn)使之在對(duì)象的生命周期內(nèi)始終保持有效,最后在對(duì)象析構(gòu)的時(shí)候釋放資源。相當(dāng)于利用 對(duì)象 管理了一份資源。這樣的優(yōu)勢(shì)在于

1.不需要顯式的釋放資源(對(duì)象析構(gòu)時(shí),自動(dòng)釋放資源)

2.采用這種方式,對(duì)象所需的資源在其生命周期內(nèi)始終保持有效。

智能指針就是一個(gè)實(shí)例出來(lái)的對(duì)象

C++98版本的庫(kù)中就提供了auto_ptr的智能指針。但是 auto_ptr存在當(dāng)對(duì)象拷貝或者賦值之后,前面的對(duì)象就懸空了。

C++11 提供更靠譜的并且支持拷貝的 shared_ptr

shared_ptr :

通過(guò)引用計(jì)數(shù)的方式實(shí)現(xiàn)多個(gè)shared_ptr 對(duì)象之間共享資源。

shared_ptr在其內(nèi)部,給每個(gè)資源都維護(hù)了著一份計(jì)數(shù),用來(lái)記錄該份資源被幾個(gè)對(duì)象共享。在對(duì)象被銷(xiāo)毀時(shí)(也就是析構(gòu)函數(shù)調(diào)用),就說(shuō)明自己不使用該資源了,對(duì)象的引用計(jì)數(shù)減一。如果引用計(jì)數(shù)是0,就說(shuō)明自己是最后一個(gè)使用該資源的對(duì)象,必須釋放該資源;如果不是0,就說(shuō)明除了自己還有其他對(duì)象在使用該份資源,不能釋放該資源,否則其他對(duì)象就成野指針了

unique_ptr :

確保一個(gè)對(duì)象同一時(shí)刻只能被一個(gè)智能指針引用,可以轉(zhuǎn)移所有權(quán)(可以從一個(gè)智能指針轉(zhuǎn)移到另一個(gè)智能指針)

auto_ptr :

C++11 已棄用, 與unique_ptr 類(lèi)似

使用時(shí),需包含頭文件

 #include <memory>

shared_ptr的使用注意事項(xiàng)

創(chuàng)建

1. 
shared_ptr<int> ptr{new int(3)};
2.
shared_ptr<int> ptr;
ptr.reset(new int(3));
3.
shared_ptr<int> ptr = make_shared<int>(3);

shared_ptr 支持使用比較運(yùn)算符,使用時(shí),會(huì)調(diào)用共享指針內(nèi)部封裝的原始指針的比較運(yùn)算符。

支持

==、!=、<、<=、>、>=

使用 比較運(yùn)算符 的前提 必須是 同類(lèi)型

示例:

shared_ptr<int> p1 = make_shared<int>(1);
shared_ptr<int> p2 = make_shared<int>(2);
shared_ptr<int> p3;
shared_ptr<double> p4 = make_shared<double>(1);

bool b1 = p1 < p2; 		// true
bool b2 = p1 > p3;		// true, 非NULL 指針與 NULL 指針相比 ,都是大于
bool b3 = p3 == p3;		// true
bool b4 = p4 < p2		// 編譯失敗,類(lèi)型不一致

shared_ptr 可以使用強(qiáng)制類(lèi)型轉(zhuǎn)換,但是不能使用普通的強(qiáng)制類(lèi)型轉(zhuǎn)換符

1.shared_ptr 強(qiáng)制類(lèi)型轉(zhuǎn)換符 允許將其中包含的指針強(qiáng)制轉(zhuǎn)換為其它類(lèi)型

2.不能使用普通的強(qiáng)制類(lèi)型轉(zhuǎn)換運(yùn)算符,否則會(huì)導(dǎo)致未定義行為

3.shared_ptr 的強(qiáng)制類(lèi)型轉(zhuǎn)換運(yùn)算符包括
static_pointer_cast
dynamic_pointer_cast
const_pointer_cast

示例:

shared_ptr<void> p(new int);	// 內(nèi)部保留 void* 指針
static_pointer_cast<int*>(p);	// 正確的 強(qiáng)制類(lèi)型轉(zhuǎn)換方式
shared_ptr<int> p1(static_cast<int*>(p.get()));	// 錯(cuò)誤的強(qiáng)制類(lèi)型轉(zhuǎn)換方式,未定義錯(cuò)誤

多個(gè) shared_ptr 不能擁有同一個(gè)對(duì)象

利用代碼理解

示例:

class Mytest{
public:
	Mytest(const string& str)
	:_str(str){}
	~Mytest(){
		std::cout << _str << "destory" << std::endl;
	}
private:
	string _str;
};

int main(){
	Mytest* p = new Mytest("shared_test");
	shared_ptr<Mytest> p1(p); 	// 該對(duì)象可以正常析構(gòu)
	shared_ptr<Mytest> p2(p); // 對(duì)象銷(xiāo)毀時(shí),錯(cuò)誤,讀取位置 0xDDDDDDDD 時(shí)發(fā)生訪(fǎng)問(wèn)沖突。
	return 0;
}

上述代碼, 共享指針 p1 對(duì)象在程序 結(jié)束時(shí),調(diào)用析構(gòu),釋放了p 所指向的空間, 當(dāng) p2 進(jìn)行析構(gòu)的時(shí)候,又釋放p所指向的空間, 但是由于已經(jīng)釋放過(guò)了, 重復(fù)釋放已經(jīng)釋放過(guò)的內(nèi)存,導(dǎo)致段錯(cuò)誤。

可以使用 shared_from_this 避免這種問(wèn)題

改進(jìn)代碼:

class Mytest:public enable_shared_from_this<Mytest> {
public:
    Mytest(const string& str)
        :_str(str) {}
    ~Mytest() {
        std::cout << _str << "destory" << std::endl;
    }
    shared_ptr<Mytest> GetSharedptr() {
        return shared_from_this();
    }
private:
    string _str;
};
int main() {
    Mytest* p = new Mytest("shared_test");
    shared_ptr<Mytest> p1(p);
    shared_ptr<Mytest> p2 = p->GetSharedptr(); // 正確做法
    
    return 0;
}

shared_ptr 的銷(xiāo)毀

shared_ptr 在初始化的時(shí)候,可以定義刪除器,刪除器可以定義為 普通函數(shù)、匿名函數(shù)、函數(shù)指針等符合要求的可調(diào)用對(duì)象

示例代碼:

void delFun(string* p) {
    std::cout << "Fun delete " << *p << endl;
    delete p;
}
int main() {

    std::cout << "begin" << std::endl;
    shared_ptr<string> p1;
    {
        shared_ptr<string> p2(new string("p1"), [](string* p) {
            std::cout << "Lamda delete " << *p << std::endl;
            delete p;
        });
        p1 = p2;
        shared_ptr<string> p3(new string("p3"), delFun);
    }
    std::cout << "end" << std::endl;
    return 0;
}

執(zhí)行結(jié)果:

begin
Fun delete p3
end
Lamda deletep1

分析結(jié)果:

首先 ,p3在{ }作用域內(nèi) ,生命周期最先結(jié)束,調(diào)用delFun作為刪除器

其次,p2 也在{ } 作用域內(nèi),生命周期也結(jié)束了,但是因?yàn)?p1 和 p2 指向了同一個(gè)對(duì)象,所以p2 銷(xiāo)毀只是將其 對(duì)象 引用計(jì)數(shù) -1。

最后,程序運(yùn)行結(jié)束,p1銷(xiāo)毀,其對(duì)象引用計(jì)數(shù)-1 變?yōu)?,調(diào)用 刪除器,銷(xiāo)毀對(duì)象。

shared_ptr<char> p(new char[10]); // 編譯能夠通過(guò),但是會(huì)造成資源泄漏
// 正確做法
shared_ptr<char> p(new char[10], [](char* p){
	delete p[];
	});
// 正確做法
shared_ptr<char> p(new char[10], default_delete<char[]>());
  • 可以為數(shù)組創(chuàng)建一個(gè)shared_ptr ,但是這樣會(huì)造成資源泄露。因?yàn)?shared_ptr 提供默認(rèn)的刪除調(diào)用的是 delete,而不是 delete[]
  • 可以使用自定義刪除器,刪除器中使用 delete[]
  • 可以使用 default_delete 作為刪除器,因?yàn)樗褂?delete[]

shared_ptr 存在的問(wèn)題:

1.循環(huán)引用
不同對(duì)象相互引用,形成環(huán)路

2.想要共享但是不想擁有對(duì)象

shared_ptr 的線(xiàn)程安全問(wèn)題

1. shared_ptr 對(duì)象中引用計(jì)數(shù)是多個(gè)shared_ptr對(duì)象共享的,兩個(gè)線(xiàn)程中shared_ptr的引用計(jì)數(shù)同時(shí)++或–,這個(gè)操作不是原子的,引用計(jì)數(shù)原來(lái)是1,++了兩次,可能還是2 這樣引用計(jì)數(shù)就錯(cuò)亂了。會(huì)導(dǎo)致資源未釋放或者程序崩潰的問(wèn)題。所以只能指針中引用計(jì)數(shù)++、–是需要加鎖的,也就是說(shuō)引用計(jì)數(shù)的操作是線(xiàn)程安全的。

2.shared_ptr 管理的對(duì)象存放在堆上,兩個(gè)線(xiàn)程中同時(shí)去訪(fǎng)問(wèn),會(huì)導(dǎo)致線(xiàn)程安全問(wèn)題。

// 1.因?yàn)榫€(xiàn)程安全問(wèn)題是偶現(xiàn)性問(wèn)題,main函數(shù)的n改大一些概率就變大了,就
容易出現(xiàn)了。
void SharePtrFunc(shared_ptr<Date>& sp, size_t n)
{
	cout << sp.Get() << endl;
	for (size_t i = 0; i < n; ++i)
	{
		// 這里智能指針拷貝會(huì)++計(jì)數(shù),智能指針析構(gòu)會(huì)--計(jì)數(shù),這里是線(xiàn)程安全的。
		shared_ptr<Date> copy(sp);
		// 這里智能指針訪(fǎng)問(wèn)管理的資源,不是線(xiàn)程安全的。所以我們看看這些值兩個(gè)線(xiàn)程++了2n次,但是最終看到的結(jié)果,并一定是加了2n
		copy->_year++;
		copy->_month++;
		copy->_day++;
	}
}
int main()
{
	shared_ptr<Date> p(new Date);
	cout << p.Get() << endl;
	const size_t n = 100;
	thread t1(SharePtrFunc, p, n);
	thread t2(SharePtrFunc, p, n);
	t1.join();
	t2.join();
	cout << p->_year << endl;
	cout << p->_month << endl;
	cout << p->_day << endl;
	return 0;
}

shared_ptr 的循環(huán)引用

struct ListNode
{
int _data;
shared_ptr<ListNode> _prev;
shared_ptr<ListNode> _next;
~ListNode(){ cout << "~ListNode()" << endl; }
};
int main()
{
shared_ptr<ListNode> node1(new ListNode);
shared_ptr<ListNode> node2(new ListNode);
cout << node1.use_count() << endl;
cout << node2.use_count() << endl;
node1->_next = node2;
node2->_prev = node1;
cout << node1.use_count() << endl;
cout << node2.use_count() << endl;
return 0;
}

循環(huán)引用代碼分析:

node1和node2兩個(gè)智能指針對(duì)象指向兩個(gè)節(jié)點(diǎn),引用計(jì)數(shù)變成1,不需要手動(dòng)delete。

node1的_next指向node2,node2的_prev指向node1,引用計(jì)數(shù)變成2。

node1和node2析構(gòu),引用計(jì)數(shù)減到1,但是_next還指向下一個(gè)節(jié)點(diǎn)。但是_prev還指向上一個(gè)節(jié)點(diǎn)。

也就是說(shuō)_next析構(gòu)了,node2就釋放了。

也就是說(shuō)_prev析構(gòu)了,node1就釋放了。

但是_next屬于node的成員,node1釋放了,_next才會(huì)析構(gòu),而node1由_prev管理,_prev屬于node2成員,所以這就叫循環(huán)引用,誰(shuí)也不會(huì)釋放。

在這里插入圖片描述

解決方案:在引用計(jì)數(shù)的場(chǎng)景下,把節(jié)點(diǎn)中的_prev和_next改成weak_ptr就可以了

原理:

node1->_next = node2;和node2->_prev = node1;時(shí)weak_ptr的_next和_prev不會(huì)增加

node1和node2的引用計(jì)數(shù)。

struct ListNode
{
	int _data;
	weak_ptr<ListNode> _prev;
	weak_ptr<ListNode> _next;
	~ListNode(){ cout << "~ListNode()" << endl; }
};
int main()
{
	shared_ptr<ListNode> node1(new ListNode);
	shared_ptr<ListNode> node2(new ListNode);
	cout << node1.use_count() << endl;
	cout << node2.use_count() << endl;
	node1->_next = node2;
	node2->_prev = node1;
	cout << node1.use_count() << endl;
	cout << node2.use_count() << endl;
	return 0;
}

unique_ptr

  • 同一個(gè)對(duì)象,只能有唯一的一個(gè) unique_ptr 指向它
  • 繼承了自動(dòng)指針 auto_ptr,
  • 有助于避免發(fā)生異常時(shí)導(dǎo)致的資源泄漏

unique_ptr的使用

unique_ptr 定義了*、-> 運(yùn)算符,沒(méi)有定義 ++ 之類(lèi)的指針?biāo)惴?/p>

unique_ptr 不允許使用賦值語(yǔ)法進(jìn)行初始化,必須使用普通指針直接初始化

unique_ptr 可以為 空

unique_ptr 不能使用普通的復(fù)制語(yǔ)義賦值, 可以使用 C++11 的 move() 函數(shù)

unique_ptr 獲得新對(duì)象時(shí),會(huì)銷(xiāo)毀之前的對(duì)象

unique_ptr 防止拷貝的原理:

// C++98防拷貝的方式:只聲明不實(shí)現(xiàn)+聲明成私有
UniquePtr(UniquePtr<T> const &);
UniquePtr & operator=(UniquePtr<T> const &);
// C++11防拷貝的方式:delete
UniquePtr(UniquePtr<T> const &) = delete;
UniquePtr & operator=(UniquePtr<T> const &) = delete;

總結(jié)

本篇文章就到這里了,希望能給你帶來(lái)幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!

相關(guān)文章

  • 使用C語(yǔ)言計(jì)算長(zhǎng)方體的表面積和體積

    使用C語(yǔ)言計(jì)算長(zhǎng)方體的表面積和體積

    這篇文章主要給大家介紹了關(guān)于如何使用C語(yǔ)言計(jì)算長(zhǎng)方體的表面積和體積的相關(guān)資料,在C語(yǔ)言中,我們可以使用乘法運(yùn)算符(*)來(lái)進(jìn)行乘法運(yùn)算,并將結(jié)果保存在一個(gè)變量中,需要的朋友可以參考下
    2023-10-10
  • C語(yǔ)言中`||`的短路機(jī)制詳解

    C語(yǔ)言中`||`的短路機(jī)制詳解

    在C語(yǔ)言中,邏輯或運(yùn)算符(||)是一種常用的邏輯運(yùn)算符,用于組合多個(gè)條件表達(dá)式,C語(yǔ)言中的邏輯或運(yùn)算符具有短路機(jī)制,這是一種非常重要的概念,本文將深入解釋C語(yǔ)言中的||短路機(jī)制以及其在編程中的應(yīng)用,感興趣的朋友跟隨小編一起看看吧
    2024-01-01
  • C++字符串類(lèi)的封裝你真的了解嗎

    C++字符串類(lèi)的封裝你真的了解嗎

    這篇文章主要為大家詳細(xì)介紹了C++字符串類(lèi)的封裝,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來(lái)幫助
    2022-02-02
  • C++ 虛函數(shù)和純虛函數(shù)的區(qū)別分析

    C++ 虛函數(shù)和純虛函數(shù)的區(qū)別分析

    這篇文章主要介紹了C++ 虛函數(shù)和純虛函數(shù)的區(qū)別,幫助大家更好的理解和學(xué)習(xí)c++的相關(guān)知識(shí),感興趣的朋友可以了解下
    2020-10-10
  • C語(yǔ)言數(shù)據(jù)結(jié)構(gòu)不掛科指南之棧&隊(duì)列&數(shù)組詳解

    C語(yǔ)言數(shù)據(jù)結(jié)構(gòu)不掛科指南之棧&隊(duì)列&數(shù)組詳解

    自考重點(diǎn)、期末考試必過(guò)指南,這篇文章讓你理解什么是棧、什么是隊(duì)列、什么是數(shù)組。文中的示例代碼講解詳細(xì),感興趣的小伙伴可以了解一下
    2022-09-09
  • C/C++判斷傳入的UTC時(shí)間是否當(dāng)天的實(shí)現(xiàn)方法

    C/C++判斷傳入的UTC時(shí)間是否當(dāng)天的實(shí)現(xiàn)方法

    在項(xiàng)目中經(jīng)常會(huì)顯示一個(gè)時(shí)間,如果這個(gè)時(shí)間在今日內(nèi)就顯示為時(shí)分秒,否則顯示為年月日,有需要的朋友可以參考一下
    2014-01-01
  • 利用Matlab一鍵生成工地海報(bào)特效

    利用Matlab一鍵生成工地海報(bào)特效

    這篇文章主要介紹了如何利用Matlab制作出工地海報(bào)的特效,文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)Matlab有一定幫助,需要的可以參考一下
    2022-03-03
  • C++實(shí)現(xiàn)掃雷游戲(控制臺(tái)版)

    C++實(shí)現(xiàn)掃雷游戲(控制臺(tái)版)

    這篇文章主要為大家詳細(xì)介紹了C++實(shí)現(xiàn)掃雷游戲,控制臺(tái)版的掃雷游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-03-03
  • VSCode同時(shí)更改所有相同的變量名或類(lèi)名的圖文教程

    VSCode同時(shí)更改所有相同的變量名或類(lèi)名的圖文教程

    這篇文章主要介紹了VSCode同時(shí)更改所有相同的變量名或類(lèi)名,本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-05-05
  • C++ HLSL實(shí)現(xiàn)簡(jiǎn)單的圖像處理功能

    C++ HLSL實(shí)現(xiàn)簡(jiǎn)單的圖像處理功能

    本文主要介紹了HLSL實(shí)現(xiàn)簡(jiǎn)單的圖像處理功能的方法,具有很好的參考價(jià)值,下面跟著小編一起來(lái)看下吧
    2017-02-02

最新評(píng)論