C++智能指針 shared_ptr/weak_ptr/unique_ptr/enable_shared_from_this

智能指針

C++11中引入了智能指針的概念,方便管理堆內存。

使用普通指針,容易造成堆內存泄露(忘記釋放),二次釋放,程序發生異常時內存泄露等問題等,使用智能指針能更好的管理堆內存。

智能指針實質是一個類對象,行爲表現的卻像一個指針。

智能指針的作用是防止忘記調用delete釋放內存和程序異常的進入catch塊忘記釋放內存。另外指針的釋放時機也是非常有考究的,多次釋放同一個指針會造成程序崩潰,這些都可以通過智能指針來解決。

智能指針在C++11版本之後提供,包含在頭文件<memory>中,shared_ptr、unique_ptr、weak_ptr。C++98有auto_ptr,到C++11中已經被摒棄。

Shared_ptr

shared_ptr允許多個指針指向相同的對象。shared_ptr使用引用計數,每一個shared_ptr的拷貝都指向相同的內存。每使用他一次,內部的引用計數加1,每析構一次,內部的引用計數減1,減爲0時,自動刪除所指向的堆內存。shared_ptr內部的引用計數是線程安全的,但是對象的讀取需要加鎖。

1 初始化。

智能指針是個模板類,可以指定類型,傳入指針通過構造函數初始化。也可以使用make_shared函數初始化。不能將指針直接賦值給一個智能指針,因爲一個是類,一個是指針。例如std::shared_ptr<int> p4 = new int(1);的寫法是錯誤的,應該改爲

int a=1;
std::shared_ptr<int> p4=make_shared<int> (a);

注意不要用一個原始指針初始化多個shared_ptr,否則會造成二次釋放同一內存。

2 拷貝和賦值。

拷貝使得對象的引用計數增加1,賦值使得原對象引用計數減1,當計數爲0時,自動釋放內存。後來指向的對象引用計數加1,指向後來的對象。

3 get()可以獲取原始指針

4 reset函數

Unique_ptr

unique_ptr“唯一”擁有其所指對象,同一時刻只能有一個unique_ptr指向給定對象(通過禁止拷貝語義、只有移動語義來實現)。

如何禁止拷貝語義:參見https://blog.csdn.net/qq_40123329/article/details/104402493中的條款6。

unique_ptr指針本身的生命週期:從unique_ptr指針創建時開始,直到離開作用域。離開作用域時,若其指向對象,則將其所指對象銷燬(默認使用delete操作符,用戶可指定其他操作)。unique_ptr指針與其所指對象的關係:在智能指針生命週期內,可以改變智能指針所指對象,如創建智能指針時通過構造函數指定、通過reset方法重新指定、通過release方法釋放所有權、通過移動語義轉移所有權。

1 release函數

2 reset

3 swap函數

4 get_deleter

Weak_ptr

weak_ptr(弱引用)是爲了配合shared_ptr(強引用)而引入的一種智能指針,因爲它不具有普通指針的行爲,沒有重載operator*和->,它的最大作用在於協助shared_ptr工作,像旁觀者那樣觀測資源的使用情況。

weak_ptr不控制對象的生命期,但是它知道對象是否還活着。如果對象還活着,那麼它可以提升爲有效的shared_ptr,如果對象已經死了,提升就會失敗,返回一個空的shared_ptr。

weak_ptr可以從一個shared_ptr或者另一個weak_ptr對象構造,獲得資源的觀測權。但weak_ptr沒有共享資源,它的構造不會引起指針引用計數的增加。使用weak_ptr的成員函數use_count()可以觀測資源的引用計數,另一個成員函數expired()的功能等價於use_count()==0,但更快,表示被觀測的資源(也就是shared_ptr的管理的資源)已經不復存在。weak_ptr可以使用一個非常重要的成員函數lock()從被觀測的shared_ptr獲得一個可用的shared_ptr對象, 從而操作資源。但當expired()==true的時候,lock()函數將返回一個存儲空指針的shared_ptr。

1 use_count

2 expired

3 lock

4 解除shared_ptr的環狀引用

T.h

#pragma once
#include <iostream>
#include <memory>

using namespace std;

class CMember;
class CLeader;

class CLeader
{
public:
	CLeader() { cout << "CLeader::CLeader()" << endl; }
	~CLeader() { cout << "CLeader::~CLeader()" << endl; }

	void setMember(const shared_ptr<CMember>& vMember) { m_Member = vMember; }
private:
	shared_ptr<CMember> m_Member;
};

class CMember
{
public:
	CMember() { cout << "CMember::CMember()" << endl; }
	~CMember() { cout << "CMember::~CMember()" << endl; }

	void setLeader(const shared_ptr<CLeader>& vLeader) { m_Leader = vLeader; }
private:
	shared_ptr<CLeader> m_Leader;
};

T.cpp

#pragma once
#include "T.h"

int main()
{
	cout << "Test" << endl;

	shared_ptr<CLeader> PtrLeader(new CLeader());
	shared_ptr<CMember> PtrMember(new CMember());

	PtrMember->setLeader(PtrLeader);
	PtrLeader->setMember(PtrMember);

	cout << "PtrLeader.use_count: " << PtrLeader.use_count() << endl;
	cout << "PtrMember.use_count: " << PtrMember.use_count() << endl;

	return 0;
}

可以看出,2個新new的對象沒有被析構掉。因爲是一個環狀引用:

爲了解決這個問題,可以採用weak_ptr來隔斷交叉引用中的迴路;所謂weak_ptr,是一種弱引用,表示只是對某個對象的一個引用和使用,而不做管理工作;

改進方案:將一個類中的shard_ptr改爲weak_ptr。

T.h

#pragma once
#include <iostream>
#include <memory>

using namespace std;

class CMember;
class CLeader;

class CLeader
{
public:
	CLeader() { cout << "CLeader::CLeader()" << endl; }
	~CLeader() { cout << "CLeader::~CLeader()" << endl; }

	void setMember(const shared_ptr<CMember>& vMember) { m_Member = vMember; }
private:
	shared_ptr<CMember> m_Member;
};

class CMember
{
public:
	CMember() { cout << "CMember::CMember()" << endl; }
	~CMember() { cout << "CMember::~CMember()" << endl; }

	void setLeader(const shared_ptr<CLeader>& vLeader) { m_Leader = vLeader; }
private:
	weak_ptr<CLeader> m_Leader;
};

可以看出,對象已被成功析構。

Enable_shared_from_this

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章