Розумні покажчики в C++
Необхідна умова: Покажчики в C++
Покажчики використовуються для доступу до зовнішніх по відношенню до програми ресурсів, як-от купа пам’яті. Отже, для доступу до пам’яті купи (якщо щось створюється в пам’яті купи) використовуються покажчики. Під час доступу до будь-якого зовнішнього ресурсу ми просто використовуємо копію ресурсу. Якщо ми вносимо в нього будь-які зміни, ми просто змінюємо його в скопійованій версії. Але якщо ми використовуємо вказівник на ресурс, ми зможемо змінити вихідний ресурс.
Проблеми зі звичайними покажчиками
Деякі проблеми зі звичайними покажчиками в C++ такі:
- Витік пам’яті: це відбувається, коли програма постійно виділяє пам’ять, але ніколи не звільняється. Це призводить до надмірного споживання пам’яті та зрештою призводить до збою системи. Висячі вказівники: висячий вказівник — це вказівник, який виникає під час вилучення об’єкта з пам’яті без зміни значення вказівника. Дикі покажчики: дикі покажчики — це покажчики, які оголошуються та виділяють пам’ять, але вказівник ніколи не ініціалізується, щоб вказувати на будь-який дійсний об’єкт або адресу. Неузгодженість даних: невідповідність даних виникає, коли деякі дані зберігаються в пам’яті, але не оновлюються узгоджено. Переповнення буфера: коли вказівник використовується для запису даних на адресу пам’яті, яка знаходиться за межами виділеного блоку пам’яті. Це призводить до пошкодження даних, якими можуть скористатися зловмисники.
приклад:
C++
// C++ program to demonstrate working of a Pointers> #include> using> namespace> std;> class> Rectangle {> private> :> > int> length;> > int> breadth;> };> void> fun()> {> > // By taking a pointer p and> > // dynamically creating object> > // of class rectangle> > Rectangle* p => new> Rectangle();> }> int> main()> {> > // Infinite Loop> > while> (1) {> > fun();> > }> }> |
Вихід
Memory limit exceeded
Пояснення: У функції веселощі , він створює вказівник, який вказує на Прямокутник об'єкт. Об'єкт Прямокутник містить два цілих числа, довжина, і ширина . Коли функція веселощі закінчується, p буде знищено, оскільки це локальна змінна. Але пам’ять, яку він спожив, не буде звільнено, оскільки ми забули використати її видалити p; в кінці функції. Це означає, що пам’ять не буде вільною для використання іншими ресурсами. Але нам більше не потрібна змінна, нам потрібна пам’ять.
У функції, основний , веселощі викликається в нескінченному циклі. Це означає, що він продовжить створювати стор . Він виділятиме все більше пам’яті, але не звільнить їх, оскільки ми не звільняли її. Витрачену пам’ять не можна використовувати знову. Що є витоком пам'яті. Все купа пам'ять може стати марною з цієї причини.
Розумні покажчики
Як ми знаємо, несвідоме невилучення вказівника викликає витік пам’яті, що може призвести до збою програми. Є мови Java, C# Механізми збору сміття щоб розумно звільнити невикористану пам’ять для повторного використання. Програмісту не потрібно турбуватися про витоки пам’яті. C++ пропонує власний механізм Розумний покажчик . Коли об'єкт знищується, він також звільняє пам'ять. Отже, нам не потрібно його видаляти, оскільки Smart Pointer впорається з цим.
А Розумний покажчик є класом-огорткою над вказівником з оператором like * і -> перевантажений. Об'єкти класу розумних покажчиків виглядають як звичайні покажчики. Але, на відміну від Звичайні покажчики, він може звільнити пам'ять знищеного об'єкта.
Ідея полягає в тому, щоб взяти урок з указкою, руйнівник, і перевантажені оператори, як * і -> . Оскільки деструктор викликається автоматично, коли об’єкт виходить за межі області видимості, динамічно виділена пам’ять буде автоматично видалена (або кількість посилань може бути зменшена).
приклад:
C++
// C++ program to demonstrate the working of Smart Pointer> #include> using> namespace> std;> class> SmartPtr {> > int> * ptr;> // Actual pointer> public> :> > // Constructor: Refer> > // techcodeview.com for use of> > // explicit keyword> > explicit> SmartPtr(> int> * p = NULL) { ptr = p; }> > // Destructor> > ~SmartPtr() {> delete> (ptr); }> > // Overloading dereferencing operator> > int> & operator*() {> return> *ptr; }> };> int> main()> {> > SmartPtr ptr(> new> int> ());> > *ptr = 20;> > cout < < *ptr;> > // We don't need to call delete ptr: when the object> > // ptr goes out of scope, the destructor for it is> > // automatically called and destructor does delete ptr.> > return> 0;> }> |
Вихід
20
Різниця між покажчиками та розумними покажчиками
| покажчик | Розумний покажчик |
|---|---|
| Покажчик — це змінна, яка зберігає адресу пам’яті, а також інформацію про тип даних про цю ділянку пам’яті. Покажчик — це змінна, яка вказує на щось у пам’яті. | Це виділений стеком об’єкт, що обертає покажчик. Простими словами, розумні покажчики — це класи, які обертають покажчик, або покажчики з областю видимості. |
| Він не знищується в будь-якій формі, коли виходить за межі своєї сфери | Він руйнує сам себе, коли виходить за межі його дії |
| Покажчики не настільки ефективні, оскільки вони не підтримують жодної іншої функції. | Розумні покажчики більш ефективні, оскільки мають додаткову функцію керування пам’яттю. |
| Вони дуже орієнтовані на працю/ручну роботу. | Вони є автоматичними/попередньо запрограмованими за своєю природою. |
Примітка: Це працює лише для внутр . Отже, нам доведеться створити Smart Pointer для кожного об’єкта? Немає , є рішення, Шаблон . У коді нижче, як ви бачите Т може бути будь-якого типу.
приклад:
C++
// C++ program to demonstrate the working of Template and> // overcome the issues which we are having with pointers> #include> using> namespace> std;> // A generic smart pointer class> template> <> class> T>> class> SmartPtr {> > T* ptr;> // Actual pointer> public> :> > // Constructor> > explicit> SmartPtr(T* p = NULL) { ptr = p; }> > // Destructor> > ~SmartPtr() {> delete> (ptr); }> > // Overloading dereferencing operator> > T& operator*() {> return> *ptr; }> > // Overloading arrow operator so that> > // members of T can be accessed> > // like a pointer (useful if T represents> > // a class or struct or union type)> > T* operator->() {> return> ptr; }> };> int> main()> {> > SmartPtr <> int> >ptr(> new> int> ());> > *ptr = 20;> > cout < < *ptr;> > return> 0;> }> |
Вихід
20
Примітка: Розумні вказівники також корисні в управлінні ресурсами, такими як дескриптори файлів або мережеві сокети.
Типи розумних покажчиків
Бібліотеки C++ забезпечують реалізації інтелектуальних покажчиків таких типів:
- auto_ptr
- унікальний_ptr
- shared_ptr
- слабкий_ptr
auto_ptr
Використовуючи auto_ptr, ви можете керувати об’єктами, отриманими з нових виразів, і видаляти їх, коли сам auto_ptr буде знищено. Коли об’єкт описується через auto_ptr, він зберігає покажчик на один виділений об’єкт.
Примітка: Цей шаблон класу застарів із C++11. unique_ptr — це новий засіб зі схожою функціональністю, але з покращеним захистом.
унікальний_ptr
унікальний_ptr зберігає лише один покажчик. Ми можемо призначити інший об’єкт, видаливши поточний об’єкт із покажчика.
приклад:
C++
// C++ program to demonstrate the working of unique_ptr> // Here we are showing the unique_pointer is pointing to P1.> // But, then we remove P1 and assign P2 so the pointer now> // points to P2.> #include> using> namespace> std;> // Dynamic Memory management library> #include> class> Rectangle {> > int> length;> > int> breadth;> public> :> > Rectangle(> int> l,> int> b)> > {> > length = l;> > breadth = b;> > }> > int> area() {> return> length * breadth; }> };> int> main()> {> // --/ Smart Pointer> > unique_ptr P1(> new> Rectangle(10, 5));> > cout // This'll print 50 // unique_ptr P2(P1); unique_ptr P2; P2 = move(P1); // This'll print 50 cout // cout return 0; }> |
Вихід
50 50
shared_ptr
З допомогою shared_ptr більше ніж один вказівник може вказувати на цей один об’єкт одночасно, і він підтримуватиме a Лічильник посилань використовуючи use_count() метод.
C++
// C++ program to demonstrate the working of shared_ptr> // Here both smart pointer P1 and P2 are pointing to the> // object Addition to which they both maintain a reference> // of the object> #include> using> namespace> std;> // Dynamic Memory management library> #include> class> Rectangle {> > int> length;> > int> breadth;> public> :> > Rectangle(> int> l,> int> b)> > {> > length = l;> > breadth = b;> > }> > int> area() {> return> length * breadth; }> };> int> main()> {> > //---/ Smart Pointer> > shared_ptr P1(> new> Rectangle(10, 5));> > // This'll print 50> > cout shared_ptr P2; P2 = P1; // This'll print 50 cout // This'll now not give an error, cout // This'll also print 50 now // This'll print 2 as Reference Counter is 2 cout < < P1.use_count() < < endl; return 0; }> |
Вихід
50 50 50 2
слабкий_ptr
Weak_ptr — це розумний вказівник, який містить невласне посилання на об’єкт. Він набагато більше схожий на shared_ptr, за винятком того, що він не підтримує a Лічильник посилань . У цьому випадку вказівник не матиме опори на об’єкт. Причина полягає в тому, що якщо припустимо, що покажчики тримають об’єкт і запитують інші об’єкти, вони можуть сформувати a Тупикова ситуація.
C++
// C++ program to demonstrate the working of weak_ptr> // Here both smart pointer P1 and P2 are pointing to the> // object Addition to which they both does not maintain> // a reference of the object> #include> using> namespace> std;> // Dynamic Memory management library> #include> class> Rectangle {> > int> length;> > int> breadth;> public> :> > Rectangle(> int> l,> int> b)> > {> > length = l;> > breadth = b;> > }> > int> area() {> return> length * breadth; }> };> int> main()> {> > //---/ Smart Pointer> > shared_ptr P1(> new> Rectangle(10, 5));> > > // create weak ptr> > weak_ptr P2 (P1);> > > // This'll print 50> > cout // This'll print 1 as Reference Counter is 1 cout < < P1.use_count() < < endl; return 0; }> |
Вихід
50 1
Бібліотеки C++ забезпечують реалізацію розумних покажчиків у формі auto_ptr, unique_ptr, shared_ptr і weak_ptr