網站首頁 編程語言 正文
在C/C++中,我們需要自己管理動態內存區,我們在寫代碼中可能會出現如下3中內存管理的缺陷
- 野指針:內存單元已經釋放,但是指向它的指針還在使用
- 重復釋放:試圖是釋放已經釋放過的內存單元
- 內存泄漏:不再使用的內存單元沒有進行釋放
C++惡心的地方就在于它存在指針,需要編寫者自己管理內存,所以內存上面的問題就會有很多,但是在其他語言,例如python,java,C#,他都不存在指針概念,也就意味著你不需要開辟釋放內存這些操作。而正因為C++將指針暴露出來,甚至將右值引用暴露出來,才使得C++的運行效率非常快。
為了減少C++中的內存問題,就出現了智能指針,它是一種對C風格指針的優化,它把內存的釋放放在了智能指針的析構函數中,這樣子就能減少一部分自己手動釋放內存的代碼。
1.C++11中的unique_ptr
#include<memory>
#include<iostream>
using namespace std;
int main()
{
? ? unique_ptr<int> up1(new int(11));
? ? unique_ptr<int> up2=up1;//無法通過編譯
? ? cout<<*up1<<endl;//11
? ? unique_ptr<int> up3=move(up1);//現在up3是數據的唯一指針
? ? cout<<*up3<<endl;//11
? ? cout<<*up1<<endl;//運行錯誤
? ? up3.reset();//釋放內存
? ? up1.reset();//不會重復釋放內存
? ? cout<<*up3<<endl;//運行錯誤
}
我們知道unique_ptr正如它的名字一樣,它表示一個對象只能由一個指針綁定,不允許一個對象同時又多個unique_ptr綁定。
而且 unique_ptr只存在移動語義,而不存在拷貝語義 ,我們看上面代碼中unique_ptr<int> up3=move(up1);,在unique_ptr中只存在移動構造函數和移動賦值函數,不存在拷貝構造函數和拷貝賦值函數。所以說我們只能用右值來構造或賦值unique_ptr。
還有一種初始化unique_ptr的方法就是:make_unique<>(),相較于使用new初始化,前者內存碎片化更少,在現代C++種主要使用,make_unique。
實際上,C++98中的auto_ptr和C++11中的unique_ptr實現的是同一個東西,但是在C++98中我們不存在移動語義,所以auto_ptr它是存在拷貝構造函數和拷貝賦值函數的,所以諸如:
auto_ptr<int> up2=up1;是可以通過編譯的,在C++11中我們廢棄掉了,auto_ptr也是這個原因。
2.C++11中的shared_ptr和weak_ptr
shared_ptr是一種共享式的指針,它采用引用計數的方式,來決定何時釋放內存,引用計數就是說,它統計每個對象有幾個指針指向它。一旦一個對象的引用計數為0,即不存在指向它的指針,那么就釋放它。
weak_ptr是用來驗證shared_ptr指向的內存單元的有效性的,被它指向的對象的引用計數不會增加。
#include<memory>
#include<iostream>
using namespace std;
void Check(weak_ptr<int>& wp)
{
? ? shared_ptr<int> sp=wp.lock();
? ? if(sp!=nullptr)
? ? ? ? cout<<"still "<<*sp<<endl;
? ? else
? ? ? ? cout<<"pointer is invalid."<<endl;
}
int main()
{
? ? shared_ptr<int> sp1=make_shared<int>(22);
? ? shared_ptr<int> sp2=sp1;
? ? weak_ptr<int> wp=sp1;
? ? cout<<*sp1<<endl;
? ? cout<<*sp2<<endl;
? ? Check(wp);
? ? sp1.reset();
? ? cout<<*sp2<<endl;
? ? Check(wp);
? ? sp2.reset();
? ? Check(wp);
}
22
22
still 22
22
still 22
pointer is invalid.
3.垃圾回收
雖然智能指針能夠幫助用戶有效管理堆內存,但是它還是需要顯式聲明智能指針,而完全不需要指針的內存管理方法也會更討人喜歡。這種方法就是垃圾回收機制,寫代碼的時候不需要開辟釋放內存操作,這些操作都由編譯器自動實現,這種智能化的方案就是垃圾回收機制。
遺憾的是,C++不支持垃圾回收機制。
垃圾回收的方式有4種
基于引用計數的方法
其實就是和shared_ptr一樣的方式,就是一旦對象的引用次數為0就釋放它,python就是使用的這種方案,不過這種方案不好,它效率比較低,一旦對象創建,或者有指針指向它,都要計算引用此時,而且它不能解決"環形引用"問題
標記-清除
這種方法就是存在一個根對象,它管理所有對象,依次遍歷每個對象,給它們引用的區域打上標記,然后遍歷完成后,把所有沒有標記的區域釋放掉,這種方案的缺陷在于會存在大量的內存碎片
標記-整理
它是在標記-清除方案的基礎上,標記完后不再遍歷釋放垃圾了,而是所有被標記的區域,向左靠齊,這樣就減少了內存碎片
標記-拷貝
它是將內存空間分為兩塊:From和to,剛開始就從From空間種分配內存,一旦From內存滿了,就把From空間中所有活對象,拷貝到to空間中,而且都是向左靠齊的,然后再將From和to的角色互換。
很遺憾C++11目前還沒有公開支持過垃圾回收,不過有些庫和有限編譯器支持了部分垃圾回收的功能
int main()
{
? ? int *p=new int;
? ? p+=10;
? ? p-=10;
? ? *p=10;
}
上面代碼中,一旦p指向了其他區域,如果你的編譯器支持垃圾回收,例如采用的引用計時方式,那么一旦p移到了其他地區,這個開辟的new int空間,就會被釋放,更危險的是,這塊空間會被其他線程使用,這時候,p如果又指回了原來的地方,那么p就是一個野指針。
為了防止,new int這塊空間被垃圾回收器回收掉,我們的一種方案是:
int main()
{
int *p=new int;
declare_reachable(p);
p+=10;
p-=10;
*p=10;
}
這里的declare_reachable函數顯式的告訴垃圾回收器,你不要取釋放這塊空間
原文鏈接:https://blog.csdn.net/m0_71009069/article/details/128806869
相關推薦
- 2022-08-02 利用Redis進行數據緩存的項目實踐_Redis
- 2022-09-15 Python淺析生成器generator的使用_python
- 2022-07-06 C語言超細致講解函數遞歸_C 語言
- 2023-01-15 React報錯Too?many?re-renders解決_React
- 2022-11-29 詳解Go語言設計模式之單例模式_Golang
- 2023-10-16 el-radio單選框,取消選中
- 2023-01-14 Go?庫bytes.Buffer和strings.Builder使用及性能對比_Golang
- 2022-06-25 React?Hooks與setInterval的踩坑問題小結_React
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支