🐶博主主页:@ᰔᩚ. 一怀明月ꦿ
❤️🔥专栏系列:线性代数,C初学者入门训练,题解C,C的使用文章,「初学」C++,linux
🔥座右铭:“不要等到什么都没有了,才下定决心去做”
🚀🚀🚀大家觉不错的话,就恳求大家点点关注,点点小爱心,指点指点🚀🚀🚀
目录
RAII
RAII(Resource Acquisition Is Initialization)是一种利用对象生命周期来控制程序资源(如内存、文件句柄、网络连接、互斥量等等)的简单技术。在对象构造时获取资源,接着控制对资源的访问使之在对象的生命周期内始终保持有效,最后在
对象析构的时候释放资源。借此,我们实际上把管理一份资源的责任托管给了一个对象。
这种做法有两大好处:
不需要显式地释放资源。
采用这种方式,对象所需的资源在其生命期内始终保持有效。
事例
template<class T> class SmartPtr { public: SmartPtr(T* ptr):_ptr(ptr) { } ~SmartPtr() { delete[] _ptr; cout<<"~ delete[] _ptr"; } T* _ptr; }; double Division(int len,int time) { if(time==0) { throw "除0错误"; } else { return (double)len/time; } } void func() { int* p1=new int[]{1,2,3}; SmartPtr<int> sp(p1);//这样将p1托管智能指针sp,当sp声明周期结束时,p1动态申请的资源就会自动释放,以前我们动态开辟内存后,我们会忘记释放内存,而现在只需要将资源托管给智能指针,就可以完成资源的自动释放 int len,time; cin>>len>>time; cout<<Division(len, time)<<endl; delete p1; } int main() { try { func(); } catch(const char* str) { cout<<str<<endl; } catch(...) { cout<<"未知异常"<<endl; } return 0; } 严格来说我们上面实现的SmartPtr不算是智能指针,还缺少指针该有的操作,例如解引用
智能指针
智能指针就是RAII的思想的具体实例,智能指针是一种用于管理动态分配内存的 C++ 类模板,它们提供了自动内存管理的功能,以减少内存泄漏和野指针的问题。
auto_ptr
std::auto_ptr
是 C++98 标准库中提供的一种智能指针,用于管理动态分配的内存资源。它是 C++ 标准库中最早引入的智能指针之一,但在 C++11 标准中已经被标记为废弃(deprecated)并在 C++17 中被移除。
std::auto_ptr
与其他智能指针不同,它采用了所有权转移(ownership transfer)的方式,即在将资源(比如动态分配的内存)赋值给另一个std::auto_ptr
后,原来的std::auto_ptr
将不再拥有该资源。这意味着只能通过拷贝构造函数或移动构造函数来转移所有权,而不能通过赋值操作符(=
)来转移所有权。由于
std::auto_ptr
存在许多问题,比如无法与标准库容器一起使用、潜在的内存泄漏问题等,因此它已经被更安全和更灵活的智能指针std::unique_ptr
所取代。以下是一个简单的示例,演示了
std::auto_ptr
的使用方式:#include <iostream> #include <memory> int main() { std::auto_ptr<int> ptr1(new int(42)); std::cout << "ptr1: " << *ptr1 << std::endl; std::auto_ptr<int> ptr2; ptr2 = ptr1; // 所有权转移 // 此时,ptr1 不再拥有资源 std::cout << "ptr2: " << *ptr2 << std::endl; // 尝试访问 ptr1,会导致未定义行为,野指针行为 // std::cout << "ptr1: " << *ptr1 << std::endl; return 0; }
auto_ptr的简单实现
//c++98 //管理权被转移之后,可能会导致被拷贝对象调用野指针 template<class T> class auto_ptr { public: auto_ptr(T* ptr):_ptr(ptr) { } //管理权转移 auto_ptr(auto_ptr<T>& ap) :_ptr(ap._ptr) { ap._ptr=nullptr; } ~auto_ptr() { cout<<"delete ptr"<<endl; delete _ptr; } //实现和指针一样 T& operator*() { return *_ptr; } T* operator->() { return _ptr; } private: T* _ptr; };
unique_ptr
std::unique_ptr
是 C++ 标准库中提供的一种智能指针,用于管理动态分配的内存资源。它是一种独占所有权的智能指针,即同一时间只能有一个std::unique_ptr
拥有对分配的资源的所有权。与
std::auto_ptr
不同,std::unique_ptr
提供了严格的所有权语义,不支持拷贝构造和拷贝赋值操作,但支持移动构造和移动赋值操作,因此它更安全、更灵活。以下是一个简单的示例,演示了
std::unique_ptr
的使用方式:#include <iostream> #include <memory> int main() { std::unique_ptr<int> ptr1(new int(42)); std::cout << "ptr1: " << *ptr1 << std::endl; // 尝试拷贝构造或拷贝赋值会导致编译错误 // std::unique_ptr<int> ptr2 = ptr1; // std::unique_ptr<int> ptr3(ptr1); // 移动构造 std::unique_ptr<int> ptr2 = std::move(ptr1); std::cout << "ptr2: " << *ptr2 << std::endl; // ptr1 不再拥有资源 // std::cout << "ptr1: " << *ptr1 << std::endl; // 错误:ptr1 为空指针 // 使用 std::unique_ptr 释放资源是自动的,不需要显式调用 delete return 0; }
unique_ptr的实现
//c++11 //unique_ptr是如何解决,管理权被转移之后,可能会导致被拷贝对象调用野指针呢? //就是禁止拷贝 // template<class T> class unique_ptr { public: unique_ptr(T* ptr):_ptr(ptr) { } ~unique_ptr() { cout<<"delete ptr"<<endl; delete _ptr; } //实现和指针一样 T& operator*() { return *_ptr; } T* operator->() { return _ptr; } //c++11的做法 unique_ptr(const unique_ptr<T>& up)=delete; unique_ptr<T>& operator=(const unique_ptr<T>& up)=delete;//赋值 //private: // //c++98的实现 // //1.只声明不实现 // //2.限定私有 // unique_ptr(const unique_ptr<T>& up); // unique_ptr<T>& operator=(const unique_ptr<T>& up);//赋值 private: T* _ptr; };
shared_ptr
std::shared_ptr
是 C++ 标准库中提供的一种智能指针,用于管理动态分配的内存资源。与std::unique_ptr
不同,std::shared_ptr
允许多个智能指针共享对同一块内存资源的所有权,通过引用计数的方式来管理资源的生命周期。当最后一个
std::shared_ptr
被销毁时,它所管理的资源会被释放。这种机制可以有效地避免内存泄漏,并且能够方便地在程序中共享数据结构。以下是一个简单的示例,演示了
std::shared_ptr
的使用方式:#include <iostream> #include <memory> int main() { // 创建一个动态分配的整数资源,被一个 shared_ptr 管理 std::shared_ptr<int> ptr1(new int(42)); std::cout << "ptr1: " << *ptr1 << std::endl; // 输出 42 { // 在作用域内创建另一个 shared_ptr 来共享同一资源 std::shared_ptr<int> ptr2 = ptr1; std::cout << "ptr2: " << *ptr2 << std::endl; // 输出 42 std::cout << "ptr1 use count: " << ptr1.use_count() << std::endl; // 输出 2,表示两个 shared_ptr 共享同一资源 } // 离开作用域后,ptr2 被销毁,但资源仍然由 ptr1 管理 std::cout << "ptr1 use count: " << ptr1.use_count() << std::endl; // 输出 1 // 最后一个 shared_ptr 被销毁时,资源被释放 return 0; }
shared_ptr的实现
//shared_ptr是通过引用计数方式来实现,拷贝和赋值 //在堆上申请一个空间作为引用计数 template<class T> class shared_ptr { public: shared_ptr(T* ptr=nullptr) :_ptr(ptr),_pcount(new int(1)) { } ~shared_ptr() { if(--(*_pcount)==0) { cout<<"delete _ptr: "<<_ptr<<endl; delete _ptr; delete _pcount; } } //实现和指针一样 T& operator*() { return *_ptr; } T* operator->() { return _ptr; } //在堆上开辟一段空间 shared_ptr(const shared_ptr<T>& sp) :_ptr(sp._ptr) ,_pcount(sp._pcount) { (*_pcount)++; } shared_ptr<T>& operator=(const shared_ptr<T>& sp) //赋值可能有很多情况 //1)sp1赋值给sp2 //2)自己给自己赋值 // 1> sp1=sp1//sp1赋值给sp1 // 2> sp1=sp2//sp1赋值给sp2 // sp1=sp2//sp1赋值给sp2 { if(_ptr!=sp._ptr)//判断是否赋值给自己 { if(--(*_pcount)==0) { delete _ptr; delete _pcount; } _ptr=sp._ptr; _pcount=sp._pcount; ++(*_pcount); } return *this; } int use_count() const { return *_pcount; } T* get() const { return _ptr; } private: T* _ptr; int* _pcount; };
weak_ptr
std::weak_ptr
也是 C++ 标准库中的智能指针,但它与std::shared_ptr
不同,它不会增加资源的引用计数,也不会拥有资源的所有权。它通常用于解决std::shared_ptr
的循环引用问题,以避免内存泄漏。
std::weak_ptr
可以从std::shared_ptr
创建,但并不会影响资源的生命周期。当需要使用资源时,可以通过std::weak_ptr
提供的lock()
函数获1个std::shared_ptr
对象,如果资源仍然存在,则返回一个指向资源的std::shared_ptr
;如果资源已经被释放,则返回一个空的std::shared_ptr
。以下是一个简单的示例,演示了
std::weak_ptr
的使用方式:#include <iostream> #include <memory> int main() { std::shared_ptr<int> sharedPtr = std::make_shared<int>(42); std::weak_ptr<int> weakPtr = sharedPtr; // 使用 weak_ptr 获取资源 if (auto sharedPtr2 = weakPtr.lock()) { std::cout << "sharedPtr2: " << *sharedPtr2 << std::endl; // 输出 42 } else { std::cout << "资源已释放" << std::endl; } // 释放原始 shared_ptr sharedPtr.reset(); // 再次尝试使用 weak_ptr 获取资源 if (auto sharedPtr3 = weakPtr.lock()) { std::cout << "sharedPtr3: " << *sharedPtr3 << std::endl; } else { std::cout << "资源已释放" << std::endl; // 输出 "资源已释放" } return 0; }
weak_ptr的实现
template<class T> class weak_ptr { public: weak_ptr() :_ptr(nullptr) {} weak_ptr(const shared_ptr<T>& sp) :_ptr(sp.get()) {} weak_ptr<T>& operator=(const shared_ptr<T>& sp) { _ptr = sp.get(); return *this; } // 像指针一样 T& operator*() { return *_ptr; } T* operator->() { return _ptr; } private: T* _ptr; };
🌸🌸🌸如果大家还有不懂或者建议都可以发在评论区,我们共同探讨,共同学习,共同进步。谢谢大家! 🌸🌸🌸