講這個之前,先來看一個例子:
十余年專注建站、設計、互聯(lián)網(wǎng)產(chǎn)品按需定制設計服務,業(yè)務涵蓋品牌網(wǎng)站設計、商城網(wǎng)站建設、小程序設計、軟件系統(tǒng)開發(fā)、成都App制作等。憑借多年豐富的經(jīng)驗,我們會仔細了解每個客戶的需求而做出多方面的分析、設計、整合,為客戶設計出具風格及創(chuàng)意性的商業(yè)解決方案,創(chuàng)新互聯(lián)更提供一系列網(wǎng)站制作和網(wǎng)站推廣的服務,以推動各中小企業(yè)全面信息數(shù)字化,并利用創(chuàng)新技術幫助各行業(yè)提升企業(yè)形象和運營效率。
void Test1 ()
{
int* p1 = new int(2);
//...
try
{
DoSomeThing();
}
catch(...)
{
delete p1 ;
throw;
}
//...
delete p1 ;
}
這個例子,是通過C++異常處理機制,來管理動態(tài)開辟出來的內(nèi)存,這是可以做到的。但是以后在開發(fā)軟件產(chǎn)品時,需要開辟動態(tài)內(nèi)存,你都這樣處理,就顯得非常繁瑣,代碼量也會加大,有時候邏輯會理不清楚,于是有人提出能不能將“動態(tài)內(nèi)存擁有權”這個問題的復雜性,從軟件產(chǎn)品本身的復雜性中分離出來,由專門的人或團隊來負責實現(xiàn),實際上是非常有利于軟件的模塊化和復用性的。畢竟,從本質(zhì)上來看,動態(tài)內(nèi)存的擁有權和一款軟件產(chǎn)品本身所要解決的目標問題在相當大程度上是正交的,將其分解開來,分而治之從軟件工程學的角度來看實在是個不錯的選擇。也顯得非常繁瑣,那C++為了處理這個問題,提出了一個叫RAII(Resource Acquisition Is Initialization)。
RAII:資源分配即初始化,定義一個類來封裝資源的分配和釋放,在構造函數(shù)完成資源的分配和初始化,在析構函數(shù)完成資源的清理,可以保證資源的正確初始化和釋放。這個類具體到下面的智能指針。
智能指針:所謂智能指針就是智能\自動化管理動態(tài)開辟內(nèi)存的釋放。
智能指針設計思想:首先提供一個類,然后用這個類去封裝原生指針,并且封裝類會提供客戶端代碼通常會施加在原生指針上的絕大多數(shù)操作接口。這樣,在保證跟原生指針相近的操作體驗的同時,簡化了動態(tài)內(nèi)存的管理負擔。
在C++11之前,標準庫中,只有一個auto_ptr,下面是模擬實現(xiàn)auto_ptr舊版本.
/*舊版本*/
/*實現(xiàn)原理:通過擁有者的改變來最后確定是否析構對象*/
#include <iostream>
using namespace std;
template<class T>
class AutoPtr
{
public:
AutoPtr(T* ptr)
:_ptr(ptr)
{
_owner = true;
}
AutoPtr(AutoPtr<T>& ap)
:_ptr(ap._ptr),_owner(ap._owner)
{
ap._owner = false;
}
AutoPtr<T>& operator=(AutoPtr<T>& ap)
{
if(this != &ap)
{
if(_owner)
{
delete _ptr;
}
_ptr = ap._ptr;
_owner = ap._owner;
if(ap._owner)
{
ap._owner = false;
}
}
return *this;
}
~AutoPtr()
{
if(_owner)
{
delete _ptr;
}
}
public:
T& operator*()
{
return *_str;
}
T* operator ->()
{
return _str;
}
private:
T* _ptr;
bool _owner;
};
void Test1()
{
AutoPtr<int> ap1(new int(1));
AutoPtr<int> ap2(ap1);
AutoPtr<int> ap3(new int(3));
ap3 = ap2;
}
int main()
{
Test1();
return 0;
}
顯示結果:
這樣看結果是對的,但是看下面這個Test2()
void Test2()
{
AutoPtr<int> ap1(new int(1));
AutoPtr<int> ap2(ap1);
AutoPtr<int> ap3(new int(3));
AutoPtr<int> ap4(ap3);
ap4 = ap1;
}
顯示結果:
看上面就出現(xiàn)問題了,你把ap1 賦給 ap4 ,但是ap4的_owner 是 false ,這就是有問題的。到最后,ap4只是指向共有的那塊空間而已,沒有達到真正的管理,再看Test3()
void Test3()
{
AutoPtr<int> ap1(new int(1));
if(1)
{
AutoPtr<int> ap2(ap1);
}
*ap1 = 10;
}
顯示結果:
程序直接崩潰了,因為產(chǎn)生了野指針的訪問,訪問的那一塊空間已經(jīng)釋放了,所以就會崩潰了,正是由于舊版本有這諸多的問題,C++改進了一下他,使他變得很“強大”,下來看模擬實現(xiàn):
/*新版本*/
/*實現(xiàn)原理:管理權的轉(zhuǎn)交*/
#include <iostream>
using namespace std;
template <class T>
class AutoPtr
{
public:
AutoPtr(T* str)
:_str(str)
{}
AutoPtr(AutoPtr<T>& ap)
:_str(ap._str)
{
ap._str = NULL;
}
AutoPtr<T>& operator=(AutoPtr<T>& ap)
{
if(this != &ap)
{
delete _str;
_str = ap._str;
ap._str = NULL;
}
return *this;
}
~AutoPtr()
{
if(_str)
delete _str;
}
public:
T& operator*()
{
return *_str;
}
T* operator ->()
{
return _str;
}
private:
T* _str;
};
struct A
{
int _a;
};
void Test4()
{
AutoPtr<int>ap(new int(1));
AutoPtr<int>ap2(ap);
AutoPtr<int>ap3(new int(2));
AutoPtr<int>ap4(ap3);
ap4 = ap;
}
int main()
{
Test4();
return 0;
}
顯示結果:
當然這個結果是正確的,但是你要這個AutoPtr又有什么用,你用對象之間拷貝構造新對象和對象之間相互賦值時,就是為了讓他們共同管理,但是現(xiàn)在,這就有點low了,既沒有達到那種共同管理,也在拷貝構造對象和相互賦值時,不清不楚,那么他就相當low。一些C++技術大牛,在開源庫函數(shù)boost庫(可移植的函數(shù)庫)中引出了新的智能指針,受到廣大編程愛好者的一致好評。于是在C++11標準引出了新的智能指針,unique_ptr,shared_ptr,weak_ptr。
模擬實現(xiàn)unique_ptr:
#include <iostream>
using namespace std;
template <class T>
class UniquePtr
{
public:
UniquePtr(T* ptr)
:_ptr(ptr)
{}
~UniquePtr()
{
if(_ptr != NULL)
{
delete _ptr;
}
}
protected:
UniquePtr(UniquePtr<T>& up);
UniquePtr<T> operator=(UniquePtr<T>& up);
public:
T* operator*()
{
return *_ptr;
}
T& operator->()
{
return _ptr;
}
private:
T* _ptr;
};
void Test1()
{
UniquePtr<int> up1(new int(1));
UniquePtr<int> up2(new int(2));
}
int main()
{
Test1();
return 0;
}
顯示結果:
沒毛病,下來看test2():
void test2()
{
UniquePtr<int> up1(new int(1));
UniquePtr<int> up2(new int(2));
up2 = up1;
UniquePtr<int> up3(up1);
}
顯示結果:
在編譯的時候出現(xiàn)了問題,這是為什么呢?
protected:
UniquePtr(UniquePtr<T>& up);
UniquePtr<T> operator=(UniquePtr<T>& up);
由于在類中只聲名拷貝構造函數(shù)和賦值運算符重載,不實現(xiàn),防止調(diào)用默認的拷貝構造函數(shù)和賦值運算符重載造成淺拷貝的問題,加上保護限制,防止在被繼承時,對其進行重寫。避免了auto_ptr出現(xiàn)的問題,我unique_ptr就是不讓被復制和賦值,他是一種防拷貝(你就是霸道)的實現(xiàn),沒有像auto_ptr那樣轉(zhuǎn)移管理權,直接就是我的就是我的,你們其他人邊去,但是我們也想要實現(xiàn)向原生指針那樣共同管理,那就可以shared_ptr,模擬實現(xiàn)shared_ptr:
#include <iostream>
using namespace std;
template <class T>
class SharedPtr
{
public:
SharedPtr(T* ptr)
:_ptr(ptr),_pcount(new int(1))
{
cout<<"SharedPtr(T* ptr)"<<endl;
}
~SharedPtr()
{
cout<<"~SharedPtr()"<<endl;
_Release();
}
SharedPtr(SharedPtr<T>& sp)
:_ptr(sp._ptr),_pcount(sp._pcount)
{
cout<<"SharedPtr(SharedPtr<T>& sp)"<<endl;
++(*_pcount);
}
SharedPtr<T>& operator= (SharedPtr<T> sp)//傳值,賦值前調(diào)用拷貝構造函數(shù)
{
cout<<"SharedPtr<T>& operator="<<endl;
swap(_ptr, sp._ptr);
swap(_pcount, sp._pcount);
return *this;
}
public:
/*接口函數(shù)*/
T& operator*()//提供*接口
{
return *_ptr;
}
T* operator->()//提供->接口
{
return _ptr;
}
public:
int UseCount()
{
return *_pcount;
}
T* GetPtr()
{
return _ptr;
}
protected:
void _Release()
{
if(--(*_pcount) == 0)
{
delete _ptr;
delete _pcount;
}
}
private:
T* _ptr;
int* _pcount;
};
int main()
{
SharedPtr<int> sp(new int(1));
SharedPtr<int> sp1(sp);
SharedPtr<int> sp2(new int(2));
SharedPtr<int> sp3(new int(3));
sp3 = sp2;
int count = sp2.UseCount();
return 0;
}
顯示結果:
第一次構造函數(shù):
SharedPtr<int> sp(new int(1));
第一次拷貝構造函數(shù):
SharedPtr<int> sp1(sp);
第二次,第三次構造函數(shù):
SharedPtr<int> sp2(new int(2));
SharedPtr<int> sp3(new int(3));
第二次拷貝構造函數(shù),第一次賦值:
sp3 = sp2;
因為賦值時,采用傳值方式,所以調(diào)用拷貝構造函數(shù)。
第一次析構函數(shù),析構臨時拷貝構造出來的臨時對象。
第二次析構函數(shù),析構sp3,sp2(是共同管理一塊內(nèi)存的)。
第三次析構函數(shù),析構sp,sp1(是共同管理一塊內(nèi)存的)。
完美...
是不是新的智能指針完美的解決auto_ptr的問題呢,對,是的。所以在以后需要使用智能指針的時候,不要用auto_ptr,因為他們完美的詮釋原生指針,當不需要復制,賦值時,應該首選unique_ptr,需要復制和賦值就需要選擇shared_ptr.
當前名稱:C++_智能指針
文章網(wǎng)址:http://www.rwnh.cn/article44/jjeche.html
成都網(wǎng)站建設公司_創(chuàng)新互聯(lián),為您提供微信小程序、營銷型網(wǎng)站建設、微信公眾號、建站公司、軟件開發(fā)、ChatGPT
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)