網站首頁 編程語言 正文
什么是RAII
RAII(Resource Acquisition Is Initialization)是由C++之父提出的,中文翻譯為資源獲取即初始化,使用局部對象來管理資源的技術稱為資源獲取即初始化;這里的資源主要是指操作系統中有限的東西如內存(heap)、網絡套接字、互斥量、文件句柄等等,局部對象是指存儲在棧的對象,它的生命周期是由操作系統來管理的,無需人工介入
RAII的原理
資源的使用一般經歷三個步驟:
- 獲取資源(創建對象)
- 使用資源
- 銷毀資源(析構對象)
但是資源的銷毀往往是程序員經常忘記的一個環節,所以程序界就想如何在程序中讓資源自動銷毀呢?解決問題的方案就是:RAII,它充分的利用了C++語言局部對象自動銷毀的特性來控制資源的生命周期
裸指針存在的問題
1.難以區分指向的是單個對象還是一個數組
2.使用完指針之后無法判斷是否應該銷毀指針,因為無法判斷指針是否”擁有“指向的對象
3.在已經確定需要銷毀指針的情況下,也無法確定是用delete關鍵字刪除,還是有其他特殊的銷毀機制,例如通過將指針傳入某個特定的銷毀函數來摧毀指針
4.即使已經確定了銷毀指針的方法,由于1的原因,仍然無法確定到底是i用delete(銷毀單個對象)還是delete[](銷毀一個數組)
5.假設上述的問題都解決了,也很難保證在代碼的所有路徑中(分支結構,異常導致的挑戰),有且僅有一次銷毀指針的操作;任何一條路徑遺漏都可能導致內存的泄露,而銷毀多次則會導致未定義行為
6.理論上沒有方法來分辨一個指針是否處于懸掛狀態
auto_ptr
class Object { int value; public: Object(int x = 0) :value(x) { cout << "Create Object:" << this << endl; } ~Object() { cout << "Destory Object:" << this << endl; } int& Value() { return value; } }; template<class _Ty> class my_auto_ptr { private: bool _Owns; _Ty* _Ptr; public: my_auto_ptr(_Ty* p = NULL) :_Owns(p != NULL), _Ptr(p) {} ~my_auto_ptr() { if (_Owns) { delete _Ptr; } _Owns = false; _Ptr = NULL; } }; void fun() { my_auto_ptr<Object> obj(new Object(10)); } int main() { fun(); }
在這里將Object構建完成后,將其指針給到p,當函數結束去調動智能指針的析構函數去釋放空間
若我們需要在fun()函數中,去調用Object類的方法obj->Value();
class Object { int value; public: Object(int x = 0) :value(x) { cout << "Create Object:" << this << endl; } ~Object() { cout << "Destory Object:" << this << endl; } int& Value() { return value; } }; template<class _Ty> class my_auto_ptr { private: bool _Owns; _Ty* _Ptr; public: my_auto_ptr(_Ty* p = NULL) :_Owns(p != NULL), _Ptr(p) {} ~my_auto_ptr() { if (_Owns) { delete _Ptr; } _Owns = false; _Ptr = NULL; } _Ty* get()const { return _Ptr; } _Ty& operator*()const { return *(get()); } _Ty* operator ->()const { return get(); } }; void fun() { my_auto_ptr<Object> obj(new Object(10)); cout << obj->Value() << endl; cout << (*obj).Value() << endl; } int main() { fun(); }
通過運算符重載,(*obj) 后將直接指向堆區(heap)的對象實體
若我們通過一個my_auto_ptr去創建另一個my_auto_ptr
class Object { int value; public: Object(int x = 0) :value(x) { cout << "Create Object:" << this << endl; } ~Object() { cout << "Destory Object:" << this << endl; } int& Value() { return value; } }; template<class _Ty> class my_auto_ptr { private: bool _Owns; _Ty* _Ptr; public: my_auto_ptr(_Ty* p = NULL) :_Owns(p != NULL), _Ptr(p) {} ~my_auto_ptr() { if (_Owns) { delete _Ptr; } _Owns = false; _Ptr = NULL; } my_auto_ptr(const my_auto_ptr& obj):_Owns(obj._Owns),_Ptr(obj._ptr) { } my_auto_ptr& operator=(const my_auto_ptr& _Y) { if(this == &_Y) return *this; if(_Owns) { delete _Ptr; } _Owns = _Y._Owns; _Ptr = _Y._Ptr; return 0; } _Ty* get()const { return _Ptr; } _Ty& operator*()const { return *(get()); } _Ty* operator ->()const { return get(); } void reset(_Ty* p = NULL) { if (_Owns) { delete _Ptr; } _Ptr = p; } _Ty* release()const { _Ty* tmp = NULL; if (_Owns) { ((my_auto_ptr*)this)->_Owns = false; //常性進行修改 tmp = _Ptr; ((my_auto_ptr*)this)->_Ptr = NULL; } return tmp; } }; void fun() { my_auto_ptr<Object> pobja(new Object(10)); my_auto_ptr<Object> pobjb(pobja); } int main() { fun(); }
如果通過淺拷貝,則兩個指針擁有同一個資源,在析構的過程會造成資源的重復釋放導致崩潰
若設置為將其資源進行轉移
my_auto_ptr(const my_auto_ptr& obj):_Owns(obj._Owns),_Ptr(release()) { } my_auto_ptr& operator=(const my_auto_ptr& _Y) { if(this == &_Y) return *this; if(_Owns) { delete _Ptr; } _Owns = _Y._Owns; _Ptr = _Y.release(); return 0; }
void fun(my_auto_ptr<Object> apx) { int x = apx->Value(); cout<<x<<endl; } int main() { my_auto_ptr<Object> pobja(new Object(10)); fun(pobja); int a = pobja->Value(); cout<<a<<endl; }
那么上面的過程中,資源會進行轉移pobja將不再擁有資源,導致pobja失去資源進而程序崩潰
這也就是auto_ptr的局限性,也導致該智能指針的幾乎沒有使用
unique_ptr
該智能指針屬于唯一性智能指針,將拷貝構造刪除,也就不能將其新建另一個對象,同時也不能作為參數傳入
class Object { int value; public: Object(int x = 0) :value(x) { cout << "Create Object:" << this << endl; } ~Object() { cout << "Destory Object:" << this << endl; } int& Value() { return value; } }; int main() { std::unique_ptr<Object> pobja(new Object(10)); //std::unique_ptr<Object> pobjb(pobja); error //不允許 std::unique_ptr<Object> pobjb(std::move(pobja)); }
通過移動賦值是可以的,通過明確的概念,對其資源進行轉移
同時unique_ptr可以區分其所指向的是一個單獨空間,或者是連續的空間
struct delete_ar_object { void operator()(Object* op) { if(op == NULL) return; delete[] op; } } int main() { std::unique_ptr<Object> pobja(new Object(10)); std::unique_ptr<Object,delete_ar_object> pobjb(new Object[10]); }
在這里如果是連續空間,會調用刪除連續空間的刪除器;單獨空間則使用默認刪除器
unique_ptr在編寫的時候,有多個模板類,分別對應單個對象的方案和一組對象的方案
并且可以通過智能指針指向fopen打開的文件對象,而文件對象是同fclose去進行關閉的
struct delete_file { void operator()(FILE *fp) { if(fp == NULL) return; fclose(fp); } } std::unique_ptr<FILE,delete_file> pfile(fopen("zyq.txt","w"));
這里只需要將默認的刪除器,更改為對文件對象的刪除器
總結
原文鏈接:https://blog.csdn.net/XXXTENTAC1ON/article/details/123824606
相關推薦
- 2022-09-07 在VScode中配置ROS環境的詳細過程_相關技巧
- 2022-09-07 Go編寫定時器與定時任務詳解(附第三方庫gocron用法)_Golang
- 2022-10-14 matlab非線性最小二乘擬合
- 2023-03-01 shell?wait等待命令的具體使用_linux shell
- 2022-08-06 python練習之循環控制語句?break?與?continue_python
- 2023-06-16 Dubbo?系列JDK?SPI?原理解析_服務器其它
- 2022-04-05 C++繼承和動態內存分配_C 語言
- 2022-08-16 Python中的枚舉函數enumerate()的具體用法_python
- 最近更新
-
- 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同步修改后的遠程分支