由於裸指針存在不少問題,主要是下面這些:ios
難以區分指向的是單個對象仍是一個數組;c++
使用完指針以後沒法判斷是否應該銷燬指針,由於沒法判斷指針是否「擁有」指向的對象;數組
在已經肯定須要銷燬指針的狀況下,也沒法肯定是用delete關鍵字刪除,仍是有其餘特殊的銷燬機制,例如經過將指針傳入某個特定的銷燬函數來銷燬指針;函數
即使已經肯定了銷燬指針的方法,因爲1的緣由,仍然沒法肯定究竟是用delete(銷燬單個對象)仍是delete[](銷燬一個數組);指針
假設上述的問題都解決了,也很難保證在代碼的全部路徑中(分支結構,異常致使的跳轉),有且僅有一次銷燬指針操做;任何一條路徑遺漏均可能致使內存泄露,而銷燬屢次則會致使未定義行爲;c++11
理論上沒有方法來分辨一個指針是否處於懸掛狀態;日誌
智能指針主要是下面幾個,c++11以後就引入到標準庫成爲語言新特性了(以前是在boost庫中)code
std::auto_ptr, std::unique_ptr對象
std::shared_ptr, std::weak_ptr內存
上述四個智能指針類型中,auto_ptr是c++ 98遺留的關鍵字,已經不建議使用,auto_ptr的功能均可以由unique_ptr更加高效的作到;本文主要先講一講unique_ptr;
unique_ptr在要表達「專屬全部權」的語義時使用,即unique_ptr指針永遠「擁有」其指向的對象,因此unique_ptr是一個move-only類型,一個unique_ptr指針是沒法被複制的,只能將「全部權」在兩個unique_ptr指針之間轉移,轉移完成後源unique_ptr將被設爲null;
定義一個unique_ptr並指向一塊動態內存:
std::unique_ptr<int> pInt(nullptr); pInt.reset(new int(1)); std::cout << *pInt << "\n"; //解引用操做與裸指針相同,打印的結果爲1
unique_ptr一個經常使用的場景是在使用工廠模式的時候,在工廠方法中返回一個unique_ptr類型的指針,例如
#pragma once //investment.h #include <assert.h> #include <memory> #include <iostream> #include <functional> //類定義 class Investment { public: virtual ~Investment() { std::cout << "investment destoryed\n"; } }; void makeLogEntry(Investment* pInv) { std::cout << "deleting investment on " << pInv << "\n"; } class Stock : public Investment { public: Stock() { std::cout << "make an invesetment on stock\n"; } virtual ~Stock() { std::cout << "a stock investment destoryed,"; } }; class Bond : public Investment { public: Bond() { std::cout << "make an investmentt on bond\n"; } virtual ~Bond() { std::cout << "a bond investment destroyed,"; } }; class RealEstate : public Investment { public: RealEstate() { std::cout << "make an investmentt on RealEstate\n"; } virtual ~RealEstate() { std::cout << "a RealEstatend investment destroyed,"; } }; void deleteAndLog(Investment* pInv) { makeLogEntry(pInv); delete pInv; } template<typename T, typename... Ts> static auto makeInvestment(Ts&&... params) { auto delInvmt = [](Investment* pInv) { makeLogEntry(pInv); delete pInv; }; typedef std::unique_ptr<Investment, decltype(delInvmt)> InvestmentPtr; std::cout << sizeof(InvestmentPtr) << "\n"; InvestmentPtr pInv(nullptr, delInvmt); pInv.reset(new T(std::forward<Ts>(params)...));//不能直接將裸指針賦值給一個unique_ptr,要使用reset return pInv; }
客戶端的調用方法
auto pInvestment = InvestmentMaker::makeInvestment<Stock>();
unique_ptr默認的銷燬方式是經過對unique_ptr中的裸指針進行delete操做,可是也能夠在聲明的時候指定銷燬函數,在上面的代碼中,經過lambda表達式置頂了一個打印日誌函數,要在銷燬指針的時候會打印日誌,
template<typename T, typename... Ts> static auto makeInvestment(Ts&&... params) { auto delInvmt = [](Investment* pInv) { makeLogEntry(pInv); delete pInv; }; typedef std::unique_ptr<Investment, decltype(delInvmt)> InvestmentPtr; InvestmentPtr pInv(nullptr, delInvmt); pInv.reset(new T(std::forward<Ts>(params)...));//不能直接將裸指針賦值給一個unique_ptr,要使用reset return pInv; }
這裏使用了c++ 14支持auto函數返回類型推導的特性,若是是c++11的話就須要把lambda表達式寫到makeInvestment方法外面了。
在使用默認delete操做的時候,unique_ptr的內存size與裸指針一致,而使用自定義銷燬方式的時候,unique_ptr的size取決於自定義銷燬的方式:
在使用函數指針的時候,unique_ptr增長一個或兩個字長;
使用函數對象的時候,unique_ptr的size取決於函數對象中的語句數量;
使用不帶capture的lambda表達式時,不增長size
void deleteAndLog(Investment* pInv) { makeLogEntry(pInv); delete pInv; } template<typename T, typename... Ts> static auto makeInvestment(Ts&&... params) { auto delInvmt = [](Investment* pInv) { makeLogEntry(pInv); delete pInv; }; typedef std::unique_ptr<Investment, decltype(delInvmt)> InvestmentPtr; std::cout << sizeof(InvestmentPtr) << "\n"; //lambda表達式不增長size, size爲4 typedef std::unique_ptr<Investment, decltype(&deleteAndLog)> FunPtrInvestmentPtr; std::cout << sizeof(FunPtrInvestmentPtr) << "\n"; //函數指針增長1一個字長, size爲8 std::function<void(Investment*)> funcionObj = deleteAndLog; typedef std::unique_ptr<Investment, decltype(funcionObj)> FunObjInvestmentPtr; std::cout << sizeof(FunObjInvestmentPtr) << "\n"; //函數對象增長大小取決於函數體的語句數量,這裏的size爲48 //... }
unique_ptr是c++11以後用來表示專屬全部權的一種智能指針,除了上面講到的這些特性以外,另外還有一個很是方便的特性就是能夠無縫地轉換成shared_ptr,以上面的代碼爲例子,在調用工廠方法的時候,能夠直接賦值給一個shared_ptr指針
std::shared_ptr<Investment> pInvestment = makeInvestment<Stock>();
shared_ptr將在下一篇博客中來說
unique_ptr是一種內存佔用少、速度快、move-only的一種智能指針,用來管理「專屬全部」語義下的動態資源;
默認的銷燬方式是經過delete,但也能夠自定義銷燬方式,根據自定義銷燬方式的不一樣幅度增長unique_ptr的size
將unique_ptr轉化爲shared_ptr是很是容易的;