您好,登錄后才能下訂單哦!
本篇內容介紹了“C++如何解決垃圾回收問題”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!
我們了解到我們在淺拷貝時對帶指針的對象進行拷貝會出現內存泄漏,那C++是否可以實現像python,JAVA一樣引入
垃圾回收機制,來靈活的來管理內存。
遺憾的是C++并不像python、java等編程語言一樣有著垃圾回收機制(Gabage Collector),因此導致了C++中對動態
存儲的管理稱為程序員的噩夢,出現了內存遺失(memory leak)、懸空指針、非法指針存取等問題。
Bjarne本人認為:
“我有意這樣設計C++,使它不依賴于自動垃圾回收(通常就直接說垃圾回收)。這是基于自己對垃圾回收系統的經驗,
我很害怕那種嚴重的空間和時間開銷,也害怕由于實現和移植垃圾回收系統而帶來的復雜性。還有,垃圾回收將使C+
+不適合做許多底層的工作,而這卻正是它的一個設計目標。但我喜歡垃圾回收的思想,它是一種機制,能夠簡化設計、
排除掉許多產生錯誤的根源。
C++中提供的構造函數和析構函數就是為了解決了自動釋放資源的需求。Bjarne有一句名言,“資源需求就是初始化(Resource Inquirment Is Initialization)”。
因此,我們可以將需要分配的資源在構造函數中申請完成,而在析構函數中釋放已經分配的資源。
在C++中,允許你控制對象的創建,清楚和復制,我們就可以通過開發一種稱為引用計數的垃圾回收機制實現這種控制
首先我們明確在對存在指針的對象構造時,析構對象時需要把指針delete(釋放掉),但是此時如果我們對對象進行淺拷貝,沒有新的指針new。
析構對象時候會出現內存泄漏(一個指針所指的內存被兩次釋放的清況),我們用通過引用計數來解決這個問題:
每構造一個對象,就創建一個新的計數器并+1.每拷貝構造一次就在被拷貝的那個對象所在的計數器上+1;
析構時候 按照構造函數析構的順序(后造先放,類似棧),最后構造或拷貝的先釋放;
每次釋放先對計數器-1并判斷計數器是否為0(是否存在淺拷貝的對象),大于0時,繼續按照析構順序析構下一個對象;
當計數器為0時,釋放指針。
我們按順序構造3個對象,計數器標號記為 1,2,3,對第一個和第三個對象淺拷貝兩次,
對對象拷貝完成后計數器1,2,3的值分別為 2 1 2.
先釋放計數器3 計數器-1后等于1,析構掉一個對象。計數器為 2 1 1
再釋放計數器1 計數器-1后等于1,析構掉一個對象。計數器為 1 1 1
再釋放計數器3 計數器-1后等于0,析構掉一個對象,并釋放掉指針。計數器為 1 1 空
再釋放計數器2 計數器-1后等于0,析構掉一個對象,并釋放掉指針。計數器為 1 空 空
再釋放計數器1 計數器-1后等于0,析構掉一個對象,并釋放掉指針。計數器為 空 空 空
最終所有對象析構完畢,指針也全部釋放完
//引用計數類 class CRefCount { public: CRefCount(); //構造計數器對象 CRefCount(const CRefCount& obj); //拷貝構造計數器 void* Alloc(int size); //構造對象時申請空間 int AddRef(); //計數增加 int ReleaseRef(); //計數減少 ~CRefCount(); private: void* m_pBuf; //指針緩沖區 int* m_pRefCount; //計數 }; CRefCount::CRefCount() { m_pBuf = nullptr; m_pRefCount = nullptr; } CRefCount::CRefCount(const CRefCount& obj) { m_pBuf = obj.m_pBuf; m_pRefCount = obj.m_pRefCount; AddRef(); } void* CRefCount::Alloc(int size) { m_pBuf = new char[size + 1]; //申請緩沖區 m_pRefCount = new int(0); AddRef(); //每次構造對象計數+1 return m_pBuf; } int CRefCount::AddRef() { if (m_pRefCount == nullptr) return 0; return ++(*m_pRefCount); } int CRefCount::ReleaseRef() { if (m_pRefCount == nullptr) return 0; return --(*m_pRefCount); } CRefCount::~CRefCount() { if (ReleaseRef() == 0) { if (m_pBuf != nullptr) { delete[] m_pBuf; m_pBuf = nullptr; delete m_pRefCount; m_pRefCount = nullptr; } } }
//student測試用例 #include"CRefCount.h" #include<iostream> #pragma warning(disable:4996) using namespace std; class CStudent { private: char* m_pName; CRefCount m_RefCount; const char* GetName() const; public: CStudent(const char* pName); }; const char* CStudent::GetName() const { return m_pName; } CStudent::CStudent(const char* pName) { m_pName = (char*)m_RefCount.Alloc(strlen(pName) + 1); //申請一個用來存放名字的空間 strcpy(m_pName, pName); } int main() { CStudent s1("shadow"); CStudent s2("iceice"); CStudent s3("maybe"); CStudent s4 = s1; CStudent s5 = s3; return 0; }
調試這個程序,我們在完成構造和拷貝后,查看內存,可以看到此時計數器1,2,3分別對應的值為2,1,2
單步跟入,看到第一個拷貝構造的對象被析構掉,計數器值-1 ,此時3個計數器值分別為為2,1,1
再繼續往后走,發現第二個拷貝對象析構掉切指針所指的內存還未被釋放掉,計數器1 -1,此時計數器值為 1,1,1
再向后執行,此時第三個構造的對象開始被析構掉同時計數器減到0,此時對象3的指針被釋放掉。
加上輔助調試代碼,最終可以看到執行結果,構造3次,拷貝2次,釋放3次,完成了引用計數功能
“C++如何解決垃圾回收問題”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。