<一>智能指針基礎

来源:https://www.cnblogs.com/erichome/archive/2022/12/02/16943460.html
-Advertisement-
Play Games

代碼1 int main(){ //裸指針,手動開闢,需要自己釋放,如果忘記了或者因為 //程式邏輯導致p沒有釋放,那麼就會導致記憶體泄漏 int *p=new int(10); if(***){ retur -1; } delete p; return 0; } 有沒有什麼辦法幫我們管理指針,確保資 ...


代碼1

int main(){  
   //裸指針,手動開闢,需要自己釋放,如果忘記了或者因為
   //程式邏輯導致p沒有釋放,那麼就會導致記憶體泄漏
   int *p=new int(10);

   if(***){
     retur -1;
   }
   delete p;
   return 0;
}

有沒有什麼辦法幫我們管理指針,確保資源釋放?
智能指針
利用棧上的對象出作用域時自動析構的特征,來做到資源的自動釋放
問題:是否可以在堆上創建裸指針?語法上沒有問題,但是我們正式希望
棧上對象出作用域能自動析構的特征來達到自動管理指針的目的,如果
將智能指針創建在堆上,那又和原來的裸指針使用遇到的問題是一樣的了
需要手動delete

代碼2

#include <iostream>
using namespace std;


template<typename T>
class MySmartPtr1 {

public:
     MySmartPtr1(T * ptr=nullptr) : _mptr(ptr) {  }
	 ~MySmartPtr1() {
		 delete _mptr;
		 _mptr = nullptr;
	 }

	 T & operator*() { return *_mptr; }//返回的 是 & , 需要修改值
	 T * operator->() { return _mptr; }

private:
	T * _mptr;

};

int main() {
	MySmartPtr1<int> ptr(new int(10));
	*ptr= 200;
	return 0;
}

代碼2的問題

int main() {
	MySmartPtr1<int> ptr(new int(10));
       
       //使用ptr 拷貝構造ptr2,預設的拷貝構造方式是值拷貝,所以底層
       //_mptr指針 指向的是同一塊記憶體,那麼ptr2 和ptr析構的時候就會有問題了,兩次析構同一片記憶體       
        MySmartPtr1<int> ptr2(ptr); 
	*mptr = 200;
	return 0;
}

如何解決呢?
1:不帶引用計數的智能指針
auto_ptr C++庫提供
C++11 新標準
scoped_ptr
unique_ptr

代碼 關於 auto_ptr

int main() {
	auto_ptr<int> ptr1(new int(100));
	auto_ptr<int> ptr2(ptr1);
	*ptr2 = 200;     
        cout<<*ptr1<<endl;//執行報錯,原因見下圖
	return 0;
}

現在不推薦使用auto_ptr
容器中推薦用auto_ptr嗎? vector<auto_ptr> v1; v2(v1); 容器的拷貝構造和容器的賦值容易引起容器元素的拷貝構造和賦值,而auto_ptr的拷貝構造會將原來管理的底層資源(指針)置空

代碼關於 scoped_ptr

int main() {	
        scope_ptr的處理方式

        scope_ptr<int>(const scope_ptr<int> & src)=delete;//通過直接和諧掉這兩個方法
        scope_ptr<int> & operator=(const scope_ptr<int> & src))=delete;//通過直接和諧掉這兩個方法
	
        return 0;
}

所以scoped_ptr使用的也很少

代碼關於 unique_ptr

int main() {	
        unique_ptr的處理方式

        unique_ptr<int>(const unique_ptr<int> & src)=delete;//通過直接和諧掉這兩個方法
        unique_ptr<int> & operator=(const unique_ptr<int> & src))=delete;//通過直接和諧掉這兩個方法
	

        unique_ptr<int> ptr1(new int(100));
	unique_ptr<int> ptr2(ptr1);//左值的拷貝構造和賦值函數已經被屏蔽了,所以編譯報錯,"嘗試使用已經刪除的函數", 要改成如下!!!


        unique_ptr<int> ptr1(new int(100));	
        unique_ptr<int> ptr2(std::move(ptr1));//編譯OK,為什麼可以呢?因為unique_ptr提供了右值引用的拷貝構造和右值引用的賦值函數,如下

        unique_ptr<int>(const unique_ptr<int> && src){};
        unique_ptr<int> & operator=(const unique_ptr<int> && src)){};


        return 0;
}

//推薦使用

2:帶引用計數的智能指針(share_ptr,weak_ptr)

帶引用計數的好處:多個智能指針可以管理同一個資源
帶引用計數:給每一個對象資源,匹配一個引用計數,
智能指針引用一個資源的時候,給這個資源引用計數加1
當這個智能指針出作用域不再使用資源的時候,給這個資源引用計數-1,當引用計數不為0的時候,還不能析構這個資源,
當引用計數為0的時候,說明已經沒有外部資源使用這個資源了,那麼就可以析構這個資源了

代碼3 簡單實現share_ptr

#include <iostream>
using namespace std;


template<typename T>
class RefCount {

public:
	RefCount(T * pSrc = nullptr, int refCount = 0):_pSrc(pSrc),_refCount(refCount) {
	
	}
	void addCount() { this->_refCount++; }
	void deleltCount() { --this->_refCount; }
	int  refCount() { return this->_refCount; }

private:
	T * _pSrc;
	int _refCount = 0;
};



template<typename T>
class MySmartPtr2 {

public:

	//新創建的智能指針,預設計數器為1
	MySmartPtr2<T> (T * mptr=nullptr): _mptr(mptr){
		_pRef = new RefCount<T>(_mptr,1);
	}

	//拷貝構造
	MySmartPtr2<T>(const MySmartPtr2<T> & _rval) {
		//兩個智能指針指向相同的資源
		
		this->_mptr = _rval._mptr;
		this->_pRef = _rval._pRef;
		this->_pRef->addCount();	
	}
	//賦值重載
	MySmartPtr2<T> & operator=(const MySmartPtr2<T> & _rval) {
		if (this == &_rval) { retur *this; }
		else {
			this->_pRef->deleltCount();
			int currentCount = this->_pRef->refCount();
			if (currentCount == 0) {
				delete this->_mptr;//銷毀指向的資源
				this->_mptr = nullptr;
				delete _pRef;
				_rPef = nullptr;
			}
			this->_pRef = _rval._pRef;
			this->_mptr = _rval._mptr;
			this->_pRef->addCount();
			return *this;
		}
	}
	~MySmartPtr2<T>() {
	
		this->_pRef->deleltCount();
		int currentCount = this->_pRef->refCount();
		if (currentCount == 0) {
			delete this->_mptr;//銷毀指向的資源
			this->_mptr = nullptr;
			delete _pRef;
			_pRef = nullptr;
		}
	}

	int getRefCount() { return this->_pRef->refCount(); }

private:
	T * _mptr;
	RefCount<T> * _pRef;
};


int main() {
	MySmartPtr2<int> ms1(new int(100)) ;
	
	{
		MySmartPtr2<int> ms2(ms1);
		cout << "RefCount=" << ms1.getRefCount() << endl;

		MySmartPtr2<int> ms3(ms1);
		cout << "RefCount=" << ms1.getRefCount() << endl;

	}
	cout << "RefCount=" << ms1.getRefCount() << endl;


	system("pause");
	
	return 0;
}

share_ptr: 強智能指針,可以改變資源的引用計數
weak_ptr: 弱智能指針,不會改變資源的引用計數

強智能指針:迴圈引用(交叉引用)是什麼問題?什麼結果?怎麼解決?

交叉引用代碼

class A{
pubic:
    A(){cout<<"A()"<<endl;}
    ~A(){cou<<"~A()"<<endl;}
    share_ptr<B> _ptrb;
}

class B{
pubic:
    B(){cout<<"B()"<<endl;}
    ~B(){cou<<"~B()"<<endl;}
    share_ptr<A> _ptrb;
}

int main(){
     
     share_ptr<A> pa(new A());
     share_ptr<B> pb(new B());
     
     pa->_ptrb=pb;
     pb->_ptra=pa;
    
     cout<<pa.use_count()<<endl;// 2
     cout<<pb.use_count()<<endl;// 2

}

出main函數的時候,
第1步 先析構pb,pb在析構的時候,發現除了自己引用B對象,其他地方(A對象內)還有引用B對象的,所以 B資源無法釋放
第2步,再析構pa,pa在析構的時候,發現除了自己引用A對象,其他地方(B對象內)還有引用A對象的,所以 導致A對象也無法釋放

出了函數這兩堆上的資源都沒有被釋放掉,泄漏!

上面代碼造成new出來的資源無法釋放!!資源泄漏問題

解決:
定義對象的時候,用強智能指針,引用對象的地方用弱智能指針

class A{
pubic:
    A(){cout<<"A()"<<endl;}
    ~A(){cou<<"~A()"<<endl;}
    void testA(){
        cout<<"A testA() Function"<<endl;
    }
    weak_ptr<B> _ptrb;
}

class B{
pubic:
    B(){cout<<"B()"<<endl;}
    ~B(){cou<<"~B()"<<endl;}

    void function(){
         share_ptr<A> _tp=_ptrb.lock();//提升方法
         if(_tp!=nullptr){
             _tp->testA();
         }         
    }
    weak_ptr<A> _ptrb; //weak_ptr 弱智能指針,不會改變引用計數
}

int main(){
     
     share_ptr<A> pa(new A());
     share_ptr<B> pb(new B());
     
     pa->_ptrb=pb;
     pb->_ptra=pa;
    
     pb.function();
     cout<<pa.use_count()<<endl;// 2
     cout<<pb.use_count()<<endl;// 2

}

share_ptr
share_ptr是C++11新添加的智能指針,它限定的資源可以被多個指針共用。

只有指向動態分配的對象的指針才能交給 shared_ptr 對象托管。將指向普通局部變數、全局變數的指針交給 shared_ptr 托管,編譯時不會有問題,但程式運行時會出錯,因為不能析構一個並沒有指向動態分配的記憶體空間的指針

weak_ptr
weak_ptr是一種用於解決shared_ptr相互引用時產生死鎖問題的智能指針。如果有兩個shared_ptr相互引用,那麼這兩個shared_ptr指針的引用計數永遠不會下降為0,資源永遠不會釋放。weak_ptr是對對象的一種弱引用,它不會增加對象的use_count,weak_ptr和shared_ptr可以相互轉化,shared_ptr可以直接賦值給weak_ptr,weak_ptr也可以通過調用lock函數來獲得shared_ptr。

weak_ptr指針通常不單獨使用,只能和 shared_ptr 類型指針搭配使用。將一個weak_ptr綁定到一個shared_ptr不會改變shared_ptr的引用計數。一旦最後一個指向對象的shared_ptr被銷毀,對象就會被釋放。即使有weak_ptr指向對象,對象也還是會被釋放。
weak_ptr並沒有重載operator->和operator *操作符,因此不可直接通過weak_ptr使用對象,典型的用法是調用其lock函數來獲得shared_ptr示例,進而訪問原始對象。

share_ptr 和 weak_ptr 是線程安全的.


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 1、參數傳遞 1.1 類名作為形參和返回值 類名——方法形參 方法的形參是類名,需要的是該類的對象;實際傳遞的是該對象的地址值 類名——返回值 方法的返回值是類名,返回的是該類的對象;實際傳遞的是該對象的地址值 示例代碼 public class Cat { public void eat(){ S ...
  • 1、 RocketMQ安裝測試 1.1 下載解壓 下載地址:https://rocketmq.apache.org/release-notes/ rocketmq-all-5.0.0-bin-release.zip 下載後上傳到伺服器; 解壓命令# unzip rocketmq-all-5.0.0- ...
  • 在SpringMVC中,控制器負責處理由DIspatchServlet接收並分發過來的請求。它把用戶請求的數據通過業務處理層封裝成一個Model,然後把該model返回給對應的View進行展示。 Controller無需繼承特定的類或實現特定的藉口。只需使用@Controller(@RestCont ...
  • 代碼1 #include <iostream> #include <thread> using namespace std; class A { public: A() { cout << "A()" << endl; } ~A() { cout << "~A()" << endl; } void ...
  • javaweb三大組件 java web:Servlet,Filter,Listener 三大組件的各自功能: Servlet:用我的話來講它就像是一個處理器,它會接受http請求然後把他封裝成 HttpServletRequest 和HttpServletResponse 對象,然後進行處理 Fi ...
  • 1,javaSript var str1="123"; var str2="123"; console.log(str1==str2);//true console.log(str1 str2);//true 這個沒什麼要說的 js裡面引入了嚴格執行的 符號,兩個等號和三個等號的區別在於 1,"== ...
  • Odoo安裝/更新模塊原理 Odoo每次安裝/更新模塊時,會進行以下幾步處理: 1.判斷是否需要創建表,如果需要創建且表不存在,則進行表的創建(不進行欄位的創建); 2.獲取該表中已經存在的欄位; 3.獲取odoo模型中的所有欄位; 4.遍歷欄位,對需要存儲的欄位,進行欄位的更新/創建,欄位屬性的更 ...
  • 前言 為什麼選擇自己開發一個詞典 市面上的詞典都不太彳亍,有Golden Dict和歐陸詞典這類註重詞典文件的,有有道詞典這類電子詞典,前者對於查詞更偏向於內容整合,後者則是釋義與例句。對於翻譯功能,兩者則都沒有整合,個人感覺很是麻煩,所以出於個人需要,決定自行開發。 開發的經歷 因為自己很喜歡UW ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...