理解C++智慧指標
C++智慧指標是面試中經常會問到的一個經典知識點,本身使用也具有很大的意義。本文從下面三個方面對智慧指標的內容進行整理,以期對智慧指標能夠有一個較為清晰的認識:
1 智慧指標的實現原理
2 常用的智慧指標
3 智慧指標的實現
1、智慧指標的實現原理:
智慧指標是一個類,且這個類是個模板類,為了適應不同基本型別的需求,它在建構函式中傳入一個普通指標,將這個基本型別指標封裝為類物件指標,並在解構函式中釋放這個指標,刪除該指標指向的記憶體空間。因為一般使用時,智慧指標的類都是區域性物件,所以當函式(或程式)自動結束時會自動被釋放。
2、 常用的智慧指標:
STL一共提供了四種智慧指標:auto_ptr,unique_ptr,shared_ptr和weak_ptr
對於所有的智慧指標需要注意以下幾點:
1) 所有的智慧指標類都有一個explicit建構函式(阻止不應該允許的經過轉換建構函式進行的隱式轉換的發生,即不能在隱式轉換中使用),因此不能直接將指標轉換為只能指標物件,必須顯式呼叫,即
int* test=new int(2);
std::shared_ptr<int> p1=test; //編譯報錯,無法從“int *”轉換
//為“std::tr1::shared_ptr<_Ty>”
//因為建構函式被宣告為explicit,必須顯式呼叫
std::shared_ptr<int> p2(test); //正確,顯式呼叫
2) 對全部三種智慧指標都應避免的一點:
string vacation("hello,world");
shared_ptr<string> pvac(&vacation); // 錯誤,No pvac過期時,程式將把delete運算子用於非堆記憶體,導致錯誤
shared_ptr<string> pvac1(new string("hello,world")); //正確
下面簡要說明四種智慧指標的特點:
1)std::auto_ptr:屬於獨佔記憶體的方式,當p1=p2;時,p2的記憶體使用權轉移給p1(p1指向p2之前所指向的地址),p2成為空懸指標(指標地址為0),若之後使用p2,可以編譯通過,但執行時會出現記憶體訪問錯誤,不安全,會出現記憶體崩潰的問題。也因此不能被放入容器中(C++11已將其摒棄)
int* test=new int(2);
std::auto_ptr<int> p1(new int(5));
printf("p1:%d\n",p1);
std::auto_ptr<int> p2(test);
std::auto_ptr<int> p3=p1; //不報編譯錯誤
printf("p3:%d\n",p3);
printf("p1:%d\n",p1);
//std::auto_ptr<int> p4(p2); //執行時報訪問錯誤
//printf("%d\n",*p1); //報訪問錯誤,因為p1將記憶體管理權轉移給p3了,p1懸空
printf("%d\n",*p2);
2)C++引入的unique_ptr,也屬於獨享記憶體所有權,但優於auto_ptr,拷貝建構函式和賦值函式只有宣告沒有定義,且為私有函式,不能使用。直接賦值會編譯出錯。需要賦值的時候用std::move
std::unique_ptr<int> p5(new int(8));
std::unique_ptr<int> p6(test);
printf("%d\n",*p5);
printf("%d\n",*p6);
//std::unique_ptr<int> p7(p5); //編譯報錯,庫內為private成員,且只宣告,未定義
//std::unique_ptr<int> p8=p6; //編譯報錯,庫內為private成員,且只宣告,未定義
//printf("%d\n",*p7);
//printf("%d\n",*p8);
std::unique_ptr<int> p9=unique_ptr<int>(test);
printf("%d\n",*p9);
printf("p9:%d\n",p9);
p6=std::move(p9);
printf("p6:%d\n",p6);
printf("p9:%d\n",p9);
//printf("%d\n",*p6); //記憶體訪問出錯,因為p6轉移了記憶體所有權
3)shared_ptr(boost、C++11)屬於共享記憶體,內部有引用計數機制(實現方式有兩種,一種是輔助類,一種是控制代碼類),對一個記憶體物件進行引用計數,當刪除其中一個指向該記憶體的指標時,引用計數減1,但並不會釋放該記憶體物件,只有當該記憶體物件的引用計數減為0時,才會釋放該塊記憶體,避免了指標空懸、記憶體訪問錯誤的情況。
std::shared_ptr<int> p10(new int(10));
std::shared_ptr<int> p11(test);
printf("%d\n",*p10);
printf("%d\n",*p11);
printf("p11->use_count:%d\n",p11.use_count());
std::shared_ptr<int> p12(p10); //編譯正常
std::shared_ptr<int> p13=p11; //編譯正常
//std::weak_ptr<int> p13=p11; //編譯正常
printf("p11->use_count:%d\n",p11.use_count());
printf("p13->use_count:%d\n",p13.use_count());
printf("p11:%d\n",p11);
printf("p13:%d\n",p13);
//p11=p13; //編譯正常
printf("p11->use_count:%d\n",p11.use_count());
printf("p13->use_count:%d\n",p13.use_count());
printf("p11:%d\n",p11);
printf("p13:%d\n",p13);
//std::shared_ptr<int> p13=test; //編譯報錯,無法從“int *”轉換為“std::tr1::shared_ptr<_Ty>”,因為建構函式被宣告為explicit,必須顯式呼叫
printf("%d\n",*p11);
printf("%d\n",*p12);
//printf("%d\n",*p13);
std::shared_ptr<int> p14=std::move(p10);
printf("%d\n",*p14);
p10 = p14;
printf("%d\n",*p10); //記憶體訪問出錯,因為p10轉移了記憶體所有權
string vacation("I wandered lonely as a cloud.");
shared_ptr<string> pvac(&vacation); // No
cout<<*pvac;
4)weak_ptr(boost、C++11)為shared_ptr設計,弱引用。只對shared_ptr進行引用,但不改變其計數;所以,當被引用的shared_ptr失效後,相應的weak_ptr也會失效。測試程式碼如下:
std::shared_ptr<int> p10(new int(10));
std::shared_ptr<int> p11(test);
printf("%d\n",*p10);
printf("%d\n",*p11);
printf("p11->use_count:%d\n",p11.use_count());
std::shared_ptr<int> p12(p10); //編譯正常
//std::shared_ptr<int> p13=p11; //編譯正常
std::weak_ptr<int> p13=p11; //編譯正常
printf("p11->use_count:%d\n",p11.use_count());
printf("p13->use_count:%d\n",p13.use_count());
printf("p11:%d\n",p11);
printf("p13:%d\n",p13);
//p11=p13; //編譯正常
printf("p11->use_count:%d\n",p11.use_count());
printf("p13->use_count:%d\n",p13.use_count());
printf("p11:%d\n",p11);
printf("p13:%d\n",p13);
//std::shared_ptr<int> p13=test; //編譯報錯,無法從“int *”轉換為“std::tr1::shared_ptr<_Ty>”,因為建構函式被宣告為explicit,必須顯式呼叫
printf("%d\n",*p11);
printf("%d\n",*p12);
//printf("%d\n",*p13);
std::shared_ptr<int> p14=std::move(p10);
printf("%d\n",*p14);
p10 = p14;
printf("%d\n",*p10); //記憶體訪問出錯,因為p10轉移了記憶體所有權
string vacation("I wandered lonely as a cloud.");
shared_ptr<string> pvac(&vacation); // No
cout<<*pvac;
看起來weak_ptr沒有什麼實質的作用,但實際上weak_ptr 主要用在軟體架構設計中,可以在基類(此處的基類並非抽象基類,而是指繼承於抽象基類的虛基類)中定義一個 boost::weak_ptr,用於指向子類的boost::shared_ptr,這樣基類僅僅觀察自己的 boost::weak_ptr 是否為空就知道子類有沒對自己賦值了,而不用影響子類 boost::shared_ptr 的引用計數,用以降低複雜度,更好的管理物件
3、 智慧指標的實現
這裡採用上文提到的輔助類的方式,具體程式碼如下:
//輔助類,用來引用計數
template<class friendclass,class T>
class U_ptr
{
friend friendclass;
T *_p;
int use_count;
U_ptr(T *p):_p(p),use_count(1)
{
cout<<"U_ptr constructor called!"<<endl;
}
~U_ptr()
{
if(use_count==0)
delete _p;
cout<<"U_ptr destructor has called!"<<endl;
}
};
template<class T>
class smartpointer
{
private:
U_ptr<smartpointer,T>* _ptr;
public:
smartpointer(T* ptr):_ptr(new U_ptr<smartpointer,T>(ptr)) //建構函式
{
cout<<"smartpointer constructor called!"<<endl;
}
T& operator *() //過載*運算子
{
return *(_ptr->_p);
}
smartpointer& operator=(const smartpointer &sptr) //過載=運算子
{
++(sptr._ptr->use_count); //在減少左運算元的使用計數之前使rhs的使用計數加1,是為了防止自身賦值
if(--_ptr->use_count==0)
delete _ptr;
_ptr=sptr._ptr;
return *this;
}
smartpointer(const smartpointer &sptr):_ptr(sptr._ptr) //拷貝建構函式
{
++_ptr->use_count;
cout<<"smartpointer copy constructor called!"<<endl;
}
int use_count() //獲取引用計數值
{
return _ptr->use_count;
}
T* get_ptr() //獲取指標地址
{
return _ptr->_p;
}
~smartpointer() //解構函式,計數為0時才釋放指標指向的記憶體
{
cout<<_ptr->_p<<" smartpointer deconstructor called!"<<endl;
cout<<"before destruction use_count is="<<use_count()<<endl;
if(--_ptr->use_count==0)
delete _ptr;
}
};
int test()
{
smartpointer<int> p1(new int(8));//a should only be use to construct once
cout<<"p1.use_count="<<p1.use_count()<<endl;
cout<<"p1="<<p1.get_ptr()<<endl;
smartpointer<int> p2(p1);
cout<<"p1.use_count="<<p1.use_count()<<endl;
cout<<"p2.use_count="<<p2.use_count()<<endl;
cout<<"p1="<<p1.get_ptr()<<endl;
cout<<"p2="<<p2.get_ptr()<<endl;
smartpointer<int> p3(p1);
cout<<"p1.use_count="<<p1.use_count()<<endl;
cout<<"p3.use_count="<<p3.use_count()<<endl;
cout<<"p1="<<p1.get_ptr()<<endl;
cout<<"p3="<<p3.get_ptr()<<endl;
smartpointer<int> p4(p3);
cout<<"p3.use_count="<<p3.use_count()<<endl;
cout<<"p4.use_count="<<p4.use_count()<<endl;
cout<<"p3="<<p3.get_ptr()<<endl;
cout<<"p4="<<p4.get_ptr()<<endl;
p4 = p4;
cout<<"p4.use_count="<<p4.use_count()<<endl;
p4 = p3;
cout<<"p4.use_count="<<p4.use_count()<<endl;
cout<<"p3.use_count="<<p3.use_count()<<endl;
cout<<"p4="<<p4.get_ptr()<<endl;
cout<<"p3="<<p3.get_ptr()<<endl;
return 0;
}
int main(){
test();
return 0;
}
執行結果如下:
與實際應用STL中的智慧指標結果相近
相關推薦
理解C++智慧指標
C++智慧指標是面試中經常會問到的一個經典知識點,本身使用也具有很大的意義。本文從下面三個方面對智慧指標的內容進行整理,以期對智慧指標能夠有一個較為清晰的認識: 1 智慧指標的實現原理 2 常用的智慧指標 3 智慧指標的實現 1、智慧指標的實現
c++智慧指標的理解
寫在前面: 智慧指標的運用是c++裡很重要的一個方面。 主要的理解: 智慧指標是為了更容易的動態的管理和使用動態記憶體而設計的,新的標準庫提供兩種智慧指標。 一個是shared_ptr 允許多個指標指向同一個物件 一個是unique_ptr 獨佔所
1.1 c/c++智慧指標
c/c++智慧指標 (直接上程式碼) template <typename T> class sharePtr { public: sharePtr() { instance_ = new T(); } ~sharePtr() { delete instan
C++ 智慧指標詳解
C++ 智慧指標詳解 一、簡介 由於 C++ 語言沒有自動記憶體回收機制,程式設計師每次 new 出來的記憶體都要手動 delete。程式設計師忘記 delete,流程太複雜,最終導致沒有 de
【轉載】C++ 智慧指標(shared_ptr/weak_ptr)原始碼分析
發現一篇對C++11智慧指標分析很透徹的文章,特轉載備忘! 以下轉載自:https://blog.csdn.net/ithiker/article/details/51532484?utm_source=blogxgwz1 C++11目前已經引入了unique_ptr, shared_pt
正確理解C語言指標中的 &a+1,假設a為一個數組
1.int a[5]={1,2,3,4,5}; int p=(int)(&a+1); printf("%d",*(p-1)); 答案為什麼是5? 這個問題的關鍵是理解 &a a是一個數組名,也就是陣列的首地址。 對a進行取地址運算子,得到的是一個指向陣列
C++ 智慧指標(一)
記憶體安全 在C++中,動態記憶體的管理是通過一對運算子來完成的:new,在動態記憶體中為物件分配空間並返回一個指向該物件的指標,我們可以選擇對物件來進行初始化;delete,接收一個動態物件的指標,銷燬該物件,並釋放與之關聯的記憶體。 動態記憶體的使用很容易出問題,因為確保
C++智慧指標的簡單剖析
導讀 最近在補看《C++ Primer Plus》第六版,這的確是本好書,其中關於智慧指標的章節解析的非常清晰,一解我以前的多處困惑。看開源的C++專案時,也能隨處看到智慧指標的影子。 下面是我在看智慧指標時所做的筆記,希望能夠解決你對智慧指標的一些困擾。 目錄 智慧指標背後的
C++智慧指標:shared_ptr,uniqe_ptr,weak_ptr
動態記憶體 在C++中,動態記憶體的管理是通過一對運算子來完成的:new和delete。 new:在動態記憶體中為物件分配空間,並返回一個指向該物件的指標 delete:接受一個動態物件的指標,銷燬該物件,並釋放與之關聯的記憶體 動態記憶體的使用需要十分小心,因為要在程式設計的時候要
C++智慧指標之unique_ptr
從C++智慧指標之auto_ptr一文中得知:在使用auto_ptr時,可能會不經意的將多個auto_ptr指向同一塊記憶體,造成auto_ptr銷燬釋放時多次釋放同一塊記憶體。為了解決該問題,本文引出了unique_ptr。 顧名思義,unique是唯一的意思。說明它跟auto_p
C++智慧指標之auto_ptr
標頭檔案<memory> 總結為一句話:auto_ptr是獨佔指標,它的出現是能夠自動析構動態分配的記憶體,避免記憶體洩漏,但是auto_ptr有很多弊端,下面會通過示例和講解一一將弊端和用法展現出來。 auto_ptr不能初始化為指向非動態記憶體(原因
C++智慧指標簡單剖析
導讀 《C++ Primer Plus》第六版,其中關於智慧指標的章節解析的非常清晰。C++面試過程中,很多面試官都喜歡問智慧指標相關的 問題,比如你知道哪些智慧指標?shared_ptr的設計原理是什麼?如果讓你自己設計一個智慧指標,你如何完成?等等……。而且在看開源的C
c++智慧指標unqiue_ptr
基本分類 在C++中智慧指標有四種,分別是auto_ptr,shared_ptr,unique_ptr,weak_ptr。其中auto_ptr已經被捨棄,不在累述。大約可以分為兩類: 一種是獨佔式擁有
C++智慧指標shared_ptr講解與使用
手動管理的弊端 在簡單的程式中,我們不大可能忘記釋放 new 出來的指標,但是隨著程式規模的增大,我們忘了 delete 的概率也隨之增大。在 C++ 中 new 出來的指標,賦值意味著引用的傳遞,當賦值運算子同時展現出“值拷貝”和“引用傳遞”兩種截然不同的語義
C++智慧指標auto_ptr使用講解
auto_ptr是C++98標準庫提供的一個智慧指標,但已被C++11明確宣告不再支援。 auto_ptr具有以下缺陷: auto_ptr有拷貝語義,拷貝後源物件變得無效,這可能引發很嚴重的問題;而unique_ptr則無拷貝語義,但提供了移動語義,這樣的錯誤
C++ 智慧指標(unique_ptr, shared_ptr)的原始碼分析
shared_ptr 標頭檔案 template <typename T> class SharedPointer { public: SharedPointer(T *ptr = nullptr, const std::function<void
c++智慧指標學習
1.標頭檔案: #include<memory> 2.要領: 注意我們訪問auto_ptr的成員函式時用的是“.” 3. auto prt ?c++呼叫getxxx()方法返回引用,是否破壞了安全性 #include <iostream> #inclu
c/c++智慧指標總結
編寫時間: 2018年10月25日12:43:11 在這裡想說一下,智慧指標的順序是通過我看到原始碼的順序來整理的。 1. boost::scoped_ptr // 這些程式碼是從別的部落格上摘取下來的 namespace boost { te
C++——智慧指標
你可能經常性的幹如下的事情。 void remodel(std::string & str) { std::string *ps = new std::string(str); str = ps; return; } 其實你也不願
c++智慧指標
靜態記憶體和棧記憶體。靜態記憶體用來儲存區域性的static物件、類的static資料成員以及定義在任何函式之外的變數。棧記憶體用來儲存定義在函式內的非static的物件。分配在靜態或者棧記憶體中的物件由編譯器自動建立和銷燬。 對於棧物件,僅在其定義的程式執行時