C++智能指针的使用
作者:英雄不问出处~
智能指针的使用场景
1. 下面的程序中,new了以后,我们也delete了,但是因为抛异常导致后面的delete没有得到执行,所以就内存泄漏了,所以我们需要new以后捕获异常,捕获到异常后delete内存,再把异常抛出。
2.但是因为new本身也可能抛异常,连续的两个new和下面的Divide都可能会抛异常,让我们处理起来很麻烦。智能指针放到这样的场景里面就让问题简单多了。
double Divide(int a, int b) { // 当b == 0时抛出异常 if (b == 0) { throw "Divide by zero condition!"; } else { return (double)a / (double)b; } } void Func() { // 这⾥可以看到如果发⽣除0错误抛出异常,另外下⾯的array和array2没有得到释放。 // 所以这⾥捕获异常后并不处理异常,异常还是交给外⾯处理,这⾥捕获了再重新抛出去。 // 但是如果array2new的时候抛异常呢,就还需要套⼀层捕获释放逻辑,这⾥更好解决⽅案 // 是智能指针,否则代码太戳了 int* array1 = new int[10]; int* array2 = new int[10];// 抛异常呢 // 连续的两个new // 如果第一个new抛异常可以捕获 // 如果第二个new抛异常,可以解决delete第二个new // 但是第一个new没有被delete // 这样就需要再第二个new的地方捕获异常,delete第一个new // 如果有很多个new呢,就太麻烦了,这里就需要智能指针了 try { int len, time; cin >> len >> time; cout << Divide(len, time) << endl; } catch (...) { cout << "delete []" << array1 << endl; cout << "delete []" << array2 << endl; delete[] array1; delete[] array2; throw; // 异常重新抛出,捕获到什么抛出什么 } // ... cout << "delete []" << array1 << endl; delete[] array1; cout << "delete []" << array2 << endl; delete[] array2; } int main() { try { Func(); } catch (const char* errmsg) { cout << errmsg << endl; } catch (const exception& e) { cout << e.what() << endl; } catch (...) { cout << "未知异常" << endl; } return 0; }
RAII和智能指针
1. RAII是获取到资源立即初始化,它是一种管理资源的类的设计思想,本质是一种利用对象生命周期来管理获取到的动态资源,避免资源泄漏,这里的资源可以是内存、文件指针、网络连接、互斥锁等等。RAII在获取资源时把资源委托给一个对象,接着控制对资源的访问,资源在对象的生命周期内始终保持有效,最后在对象析构的时候释放资源,这样保障了资源的正常释放,避免资源泄漏问题。
2. RAII像指针一样
在对象销毁后会正常的释放资源
template<class T> class SmartPtr { public: // RAII SmartPtr(T* ptr) :_ptr(ptr) {} ~SmartPtr() { cout << "delete[] " << _ptr << endl; delete[] _ptr; } private: T* _ptr; }; double Divide(int a, int b) { // 当b == 0时抛出异常 if (b == 0) { throw "Divide by zero condition!"; } else { return (double)a / (double)b; } } void Func() { // 这⾥可以看到如果发⽣除0错误抛出异常,另外下⾯的array和array2没有得到释放。 // 所以这⾥捕获异常后并不处理异常,异常还是交给外⾯处理,这⾥捕获了再重新抛出去。 // 但是如果array2new的时候抛异常呢,就还需要套⼀层捕获释放逻辑,这⾥更好解决⽅案 // 是智能指针,否则代码太戳了 SmartPtr<int> p1 = new int[10]; SmartPtr<int> p2 = new int[10];// 抛异常呢 SmartPtr<int> p3 = new int[10]; int len, time; cin >> len >> time; cout << Divide(len, time) << endl; } int main() { try { Func(); } catch (const char* errmsg) { cout << errmsg << endl; } catch (const exception& e) { cout << e.what() << endl; } catch (...) { cout << "未知异常" << endl; } return 0; }
智能指针类除了满足RAII的设计思路,还要方便资源的访问,所以智能指针类还像迭代器类一样重载 operator*/operator->/operator[] 等运算符,方便访问资源。
template<class T> class SmartPtr { public: // RAII SmartPtr(T* ptr) :_ptr(ptr) {} ~SmartPtr() { cout << "delete[] " << _ptr << endl; delete[] _ptr; } // 重载运算符,模拟指针的⾏为,⽅便访问资源 T& operator*() { return *_ptr; } T* operator->() { return _ptr; } T& operator[](size_t i) { return _ptr[i]; } private: T* _ptr; }; void Func() { p1[1] = 50; p4->first = 1; p4->second = 2; cout << p1[1] << endl; }
C++标准库智能指针的使用
浅拷贝的问题,共同管理同一块资源,而深拷贝是两个指针管理不同的资源了,现在要求两个指针管理同一块资源?
int main() { // 需要p1和p2同时管理同一块资源,浅拷贝 // 析构多次的问题如何解决? SmartPtr<int> p1 = new int[10]; SmartPtr<int> p2(p1); return 0; }
1.C++标准库中的智能指针都在< memory >这个头文件下,智能指针有好几种,除了weak_ptr他们都符合RAII和像指针一样访问的行为,原理上而言主要是解决智能指针拷贝时的思路不同。
2. C++98的智能指针
auto_ptr是C++98时设计出来的智能指针,它的特点是拷贝时把被拷贝对象的资源的管理权转移给拷贝对象,这是⼀个非常糟糕的设计,因为他会把被拷贝对象悬空(相当于被拷贝对象是空指针了),访问报错的问题,C++11设计出新的智能指针后,强烈建议不要使用auto_ptr
struct Date { int _year; int _month; int _day; Date(int year = 1, int month = 1, int day = 1) :_year(year) , _month(month) , _day(day) {} // 本身不需要析构,为了验证auto_ptr是否可以解决析构两次的问题 ~Date() { cout << "~Date()" << endl; } }; int main() { // 拷⻉时,管理权限转移,被拷⻉对象ap1悬空 auto_ptr<Date> ap1(new Date); auto_ptr<Date> ap2(ap1); // 空指针访问,ap1对象已经悬空 // ap1->_year++; return 0; }
C++11的智能指针
2、unique_ptr是唯一指针,他的特点是不支持拷贝,只支持移动。如果不需要拷贝的场景就非常建议使用它。把拷贝构造和拷贝赋值给(封)delete了
int main() { unique_ptr<Date> up1(new Date); 不支持拷贝 // unique_ptr<Date> up2(up1); 支持移动,移动后up1也悬空了 unique_ptr<Date> up3(move(up1)); return 0; }
3、shared_ptr共享指针,他的特点是支持拷贝,也支持移动。如果需要拷贝的场景就需要使用他了。底层是用引用计数的方式实现的。
int main() { shared_ptr<Date> p1(new Date); shared_ptr<Date> p2(p1); shared_ptr<Date> p3(p1); cout << p1.use_count() << endl;// 3 p1->_day++; cout << p1->_day << endl; // 2 cout << p2->_day << endl; // 2 cout << p3->_day << endl; // 2 return 0; }
4、weak_ptr弱(辅助解决shared_ptr的一个问题)指针,它不同于上面的指针,它不支持RAII,也就意味着不能用它直接管理资源,weak_ptr的产生本质是要解决shared_ptr的一个循环引用导致内存泄漏的问题。
到此这篇关于C++智能指针的使用的文章就介绍到这了,更多相关C++智能指针内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!