Use scenarios for smart pointers
1. In the following program, after new, we deleted it, but because the exception is thrown, the subsequent delete is not executed, so the memory leaks. Therefore, we need to catch the exception after new, delete the memory after catching the exception, and then throw the exception.
2. However, because the new itself may throw exceptions, the two consecutive new and the Divide below may throw exceptions, which makes it very troublesome to deal with. Putting smart pointers in such a scenario makes the problem much simpler.
double Divide(int a, int b) { // When b == 0 exception is thrown if (b == 0) { throw "Divide by zero condition!"; } else { return (double)a / (double)b; } } void Func() { // This can be seen that if an error except 0 is thrown, the following array and array2 are not released. // So this does not handle the exception after catching the exception, and the exception is still left to the outside world to handle. This is caught and then thrown again. // But if an exception is thrown when array2new, you still need to install a layer of capture and release logic, which is a better solution // It's a smart pointer, otherwise the code will be too poked int* array1 = new int[10]; int* array2 = new int[10];// What about throwing an exception // Two consecutive new // If the first new throw exception can be caught // If the second new throws an exception, you can solve the problem of delete the second new // But the first new is not deleted // In this way, you need to catch the exception in the second new place and delete the first new one // If there are many new ones, it will be too troublesome, and smart pointers are needed here 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; //Exception is re-throwed, what is caught and what is thrown } // ... 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 << () << endl; } catch (...) { cout << "Unknown exception" << endl; } return 0; }
RAII and smart pointers
1. RAII is to obtain resources and is an instant initialization. It is a design idea for managing resources. Its essence is to use the object life cycle to manage the acquired dynamic resources to avoid resource leakage. The resources here can be memory, file pointers, network connections, mutex locks, etc.RAII delegates resources to an object when obtaining resources, then control access to resources,Resources remain valid throughout the life of an object, and finally release resources when object destruction, thus ensuring the resourceNormal release, avoid resource leakage problems.
2. RAII is like a pointer
Resources will be released normally after the object is destroyed
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) { // When b == 0 exception is thrown if (b == 0) { throw "Divide by zero condition!"; } else { return (double)a / (double)b; } } void Func() { // This can be seen that if an error except 0 is thrown, the following array and array2 are not released. // So this does not handle the exception after catching the exception, and the exception is still left to the outside world to handle. This is caught and then thrown again. // But if an exception is thrown when array2new, you still need to install a layer of capture and release logic, which is a better solution // It's a smart pointer, otherwise the code will be too poked SmartPtr<int> p1 = new int[10]; SmartPtr<int> p2 = new int[10];// What about throwing an exception 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 << () << endl; } catch (...) { cout << "Unknown exception" << endl; } return 0; }
In addition to meeting the design ideas of RAII, the smart pointer class also needs to facilitate resource access, soSmart pointer class is like iterator classOverload operators such as operator*/operator->/operator[] to facilitate access to resources.
template<class T> class SmartPtr { public: // RAII SmartPtr(T* ptr) :_ptr(ptr) {} ~SmartPtr() { cout << "delete[] " << _ptr << endl; delete[] _ptr; } // Overload operators to simulate the behavior of pointers, facilitate access to resources 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; }
Use of smart pointers in C++ standard library
The problem of shallow copy is to jointly manage the same resource, while deep copy is to manage different resources with two pointers. Now two pointers are required to manage the same resource?
int main() { //P1 and p2 need to manage the same resource at the same time, and copy it // How to solve the problem that has been destructed multiple times? SmartPtr<int> p1 = new int[10]; SmartPtr<int> p2(p1); return 0; }
++The smart pointers in the standard library are all in the header file <memory>. There are several smart pointers. Except for weak_ptr, they all conform to RAII and access behaviors like pointers. In principle, they are mainly used to solve the smart pointer.Different ideas when copying。
2. C++98 smart pointer
auto_ptr is a smart pointer designed in C++98.Its characteristic is that when copying, the management rights of the copied object's resources are transferred to the copied object.This is a very bad design.Because he will hang the copied object in the air(It is equivalent to the copy object being null pointer). After the access error is reported, after C++11 designs a new smart pointer, it is strongly recommended not to use 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) {} // Destruction is not required by itself. In order to verify whether auto_ptr can solve the problem of destruction twice ~Date() { cout << "~Date()" << endl; } }; int main() { // When copying, the management permissions are transferred, and the copied object ap1 is hung empty auto_ptr<Date> ap1(new Date); auto_ptr<Date> ap2(ap1); // Access the null pointer, the ap1 object is already empty // ap1->_year++; return 0; }
C++11 smart pointer
2. Unique_ptr is the only pointer.Its characteristic is that it does not support copying, only supports mobile. It is highly recommended if you don't need a copy. Assign copy constructs and copy values to (block) delete
int main() { unique_ptr<Date> up1(new Date); Copying is not supported // unique_ptr<Date> up2(up1); Support mobile,After movingup1Also hanging unique_ptr<Date> up3(move(up1)); return 0; }
3. shared_ptr shared pointer, its characteristics areSupports copying and also supports mobile. If neededHe needs to be used for copying scenes. The bottom layer is usedReference counting methodof.
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 weak (assisted in solving a problem with shared_ptr) pointer, which is different from the above pointer. It does not support RAII, which means that it cannot be used to directly manage resources.The essence of weak_ptr generation is to solve the problem of memory leakage caused by a circular reference of shared_ptr.。
This is the end of this article about the use of C++ smart pointers. For more related C++ smart pointers, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!