Чи є гарною практикою NULL покажчик після його видалення?


150

Почну, кажу, використовуйте розумні вказівники, і вам ніколи про це не доведеться хвилюватися.

Які проблеми з наведеним нижче кодом?

Foo * p = new Foo;
// (use p)
delete p;
p = NULL;

Це було викликано відповіддю та коментарями до іншого питання. Один коментар Ніла Баттерворта приніс кілька результатів:

Встановлення покажчиків на NULL після видалення не є універсальною хорошою практикою в C ++. Бувають випадки, коли це добре робити, і періоди, коли це безглуздо і може приховати помилки.

Існує маса обставин, коли це не допоможе. Але, на мій досвід, це не зашкодить. Хтось мене просвітить.


6
@Andre: Технічно це не визначено. Що може статися, це те, що ви отримуєте доступ до тієї ж пам’яті, що і раніше, але тепер вона може використовуватися чимось іншим. Якщо ви видалите пам'ять двічі, це, ймовірно, накрутить виконання вашої програми важко знайти. Це безпечний для deleteнульового вказівника, хоча це одна з причин, що нульовий покажчик може бути хорошим.
Девід Торнлі

5
@ Андре Пена, це не визначено. Часто це навіть не повторюється. Ви встановите вказівник на NULL, щоб помилка була помітнішою під час налагодження, а може бути, щоб зробити її більш повторюваною.
Марк Викуп

3
@ Андре: Ніхто не знає. Це невизначена поведінка. Це може призвести до збоїв із порушенням доступу або може перезаписати пам'ять, яку використовує решта програми. Мовний стандарт не дає гарантій того, що станеться, і тому ви не можете довіряти вашій заявці, як тільки це відбулося. Це могло випустити ядерні ракети або відформатувати ваш жорсткий привід. це може пошкодити пам’ять вашого додатка, або може змусити демонів вилетіти з вашого носа. Усі ставки знищені.
jalf

16
Літаючі демони - це особливість, а не помилка.
jball

3
Це питання не є дублікатом, оскільки інше питання стосується C, а це - про C ++. Дуже багато відповідей залежать від таких речей, як розумні вказівники, які недоступні на C ++.
Адріан Маккарті

Відповіді:


86

Встановлення вказівника на 0 (що є "null" у стандартному C ++, значення NULL від C дещо інше) дозволяє уникнути збоїв на подвійних видаленнях.

Розглянемо наступне:

Foo* foo = 0; // Sets the pointer to 0 (C++ NULL)
delete foo; // Won't do anything

При цьому:

Foo* foo = new Foo();
delete foo; // Deletes the object
delete foo; // Undefined behavior 

Іншими словами, якщо ви не встановите видалені покажчики на 0, у вас виникнуть проблеми, якщо ви робите подвійне видалення. Аргументом проти встановлення покажчиків на 0 після видалення буде те, що це маскує подвійне видалення помилок і залишає їх без обробки.

Очевидно, що помилок подвійного видалення, очевидно, очевидно, але залежно від семантики власності та життєвих циклів об'єктів, цього на практиці важко досягти. Я віддаю перевагу помилку подвійного видалення в масках через UB.

Нарешті, сторонне позначення щодо управління розподілом об’єктів, я пропоную вам ознайомитись із std::unique_ptrсуворим / єдиним правом власності, std::shared_ptrспільним володінням або іншою реалізацією інтелектуального вказівника, залежно від ваших потреб.


13
Ваша програма не завжди вийде з ладу при подвійному видаленні. Залежно від того, що відбувається між двома видаленими, все може статися. Швидше за все, ви зіпсуєте свою купу, і в якийсь момент згорнуться в абсолютно незв’язаний фрагмент коду. Хоча segfault зазвичай краще, ніж мовчки ігнорувати помилку, segfault в цьому випадку не гарантується, і це сумнівна корисність.
Адам Розенфілд

28
Проблема тут полягає в тому, що у вас є подвійне видалення. Здійснення покажчика NULL просто приховує той факт, що він не виправляє його або робить його більш безпечним. Уявіть собі, що він піде через рік і побачив, що foo видалений. Тепер він вважає, що може повторно використовувати вказівник, на жаль, він може пропустити друге видалення (можливо, навіть не в тій же функції), і тепер повторне використання вказівника тепер втрачається під час другого видалення. Будь-який доступ після другого видалення є основною проблемою.
Мартін Йорк

11
Це правда, якщо встановлення покажчика NULLможе маскувати помилку подвійного видалення. (Дехто може вважати цю маску насправді рішенням - вона є, але не дуже вдалою, оскільки вона не потрапляє в корінь проблеми.) Але не встановлювати її на NULL маскує далеко (FAR!) Більше поширені проблеми доступу до даних після їх видалення.
Адріан Маккарті

AFAIK, std :: auto_ptr був застарілий у майбутньому стандарті c ++
rafak

Я б не сказав, що застаріло, це здається, що ідея просто пропала. Скоріше, його замінюють на те unique_ptr, що робить те, що auto_ptrнамагався зробити, з рухомою семантикою.
GManNickG

55

Якщо встановити покажчики на NULL після того, як ви видалили те, на що вказувалося, це, безумовно, не може зашкодити, але це часто є невід'ємною допомогою для більш фундаментальної проблеми: Чому ви в першу чергу використовуєте покажчик? Я бачу дві типові причини:

  • Ви просто хотіли, щоб щось було виділено на купі. У такому випадку загортання його в об'єкт RAII було б набагато безпечнішим та чистішим. Закінчіть область об’єкта RAII, коли об’єкт більше не потрібен. Ось так std::vectorпрацює, і це вирішує проблему випадкового залишення покажчиків на розселену пам’ять навколо. Покажчиків немає.
  • Або, можливо, вам потрібна складна семантика спільної власності. Вказівник, повернутий з якого, newможе бути не таким, як той, що deleteвикликається. Кілька об'єктів, можливо, тим часом використовували об'єкт. У такому випадку кращим буде загальний покажчик або щось подібне.

Моє правило: якщо ви залишаєте покажчики в коді користувача, ви робите це неправильно. Вказівник не повинен бути там, щоб в першу чергу вказувати на сміття. Чому не існує об'єкта, який несе відповідальність за забезпечення його дійсності? Чому сфера його дії не закінчується, коли об'єкт із вказаним об'єктом робить?


15
Отже, ви наводите аргумент, що в першу чергу не повинно бути жодного вказівника, і нічого, що стосується зазначеного вказівника, не повинно бути благословлено терміном "хороша практика"? Досить справедливо.
Марк Викуп

7
Ну, більш-менш. Я б не сказав, що нічого, що стосується необробленого покажчика, не можна назвати доброю практикою. Просто, це швидше виняток, а не правило. Зазвичай наявність покажчика - це показник того, що на глибшому рівні щось не так.
jalf

3
але, щоб відповісти на негайне запитання, ні, я не бачу, як встановлення покажчиків на нуль коли-небудь може спричинити помилки.
jalf

7
Я не згоден - бувають випадки, коли вказівник добре використовувати. Наприклад, на стеці є дві змінні, і ви хочете вибрати одну з них. Або ви хочете передати додаткову змінну функції. Я б сказав, ви ніколи не повинні використовувати сирий покажчик спільно з new.
rlbond

4
коли вказівник вийшов за межі, я не бачу, як щось або комусь може знадобитися для цього.
джельф

43

У мене є ще краща найкраща практика: Де можливо, закінчіть область застосування змінної!

{
    Foo* pFoo = new Foo;
    // use pFoo
    delete pFoo;
}

17
Так, RAII - твій друг. Загорніть його в клас, і це стане ще простіше. Або взагалі не обробляти пам'ять, використовуючи STL!
Брайан

24
Так, це найкращий варіант. Однак не відповідає на питання.
Марк Викуп

3
Це, здається, є лише побічним продуктом використання періоду областей функцій, і насправді не вирішує цю проблему. Коли ви використовуєте вказівники, ви зазвичай передаєте їх копії в кілька шарів глибиною, і тоді ваш метод справді безглуздий, намагаючись вирішити проблему. Хоча я згоден, що хороший дизайн допоможе вам виділити помилки, я не думаю, що ваш метод є основним засобом для досягнення цієї мети.
San Jacinto

2
Подумайте про це, якщо ви могли це зробити, чому б ви просто не забули купу і не витягнули всю свою пам’ять зі стека?
Сан-Хасінто

4
Мій приклад навмисно мінімальний. Наприклад, замість нового, можливо, об'єкт створюється фабрикою, і в цьому випадку він не може перейти на стек. А може, він створений не на початку сфери, а знаходиться в якійсь структурі. Що я ілюструю, це те, що цей підхід знайде будь-яке неправомірне використання покажчика під час компіляції , тоді як при NULLing він знайде будь-яке неправильне використання під час виконання .
Дон Нойфельд

31

Я завжди встановлюю вказівник на NULL(зараз nullptr) після видалення об'єкта (ів), на який він вказує.

  1. Це може допомогти зафіксувати багато посилань на звільнену пам'ять (якщо припустити, що ваша платформа виявила помилки на дереві нульового вказівника).

  2. Він не зафіксує всі посилання на вільну пам'ять, якщо, наприклад, у вас є копії вказівника. Але деякі краще, ніж ніхто.

  3. Це маскує подвійне видалення, але я вважаю, що це набагато рідше, ніж доступ до вже звільненої пам'яті.

  4. У багатьох випадках компілятор збирається оптимізувати його. Тож аргумент, що це зайве, не переконує мене.

  5. Якщо ви вже використовуєте RAII, то deleteу вашому коді не так багато s, тому аргумент того, що додаткове призначення викликає захаращення, не переконує мене.

  6. Часто зручно під час налагодження бачити нульове значення, а не застарілий покажчик.

  7. Якщо це все-таки турбує вас, використовуйте натомість розумний вказівник або посилання.

Я також встановлюю інші типи ручок ресурсу для значення без ресурсу, коли ресурс вільний (що, як правило, лише в деструкторі оболонки RAII, написаному для інкапсуляції ресурсу).

Я працював над великим комерційним продуктом (9 мільйонів тверджень) (насамперед на С). Одного разу ми застосували макро-магію, щоб обнулити покажчик при звільненні пам'яті. Це негайно виявило безліч ховаючих помилок, які були негайно виправлені. Наскільки я пам'ятаю, у нас ніколи не було помилок з подвійною вільністю.

Оновлення: Microsoft вважає, що це хороша практика безпеки та рекомендує цю практику в політиці SDL. Мабуть, MSVC ++ 11 автоматично заглушить видалений вказівник (за багатьох обставин), якщо ви компілюєте параметр / SDL.


12

По-перше, існує багато питань щодо цієї та тісно пов'язаних тем, наприклад, чому б не видалити встановити вказівник на NULL? .

У вашому коді питання, що відбувається далі (використовуйте p). Наприклад, якщо десь у вас є такий код:

Foo * p2 = p;

то встановлення p на NULL виконується дуже мало, так як у вас ще є вказівник p2, про який потрібно турбуватися.

Це не означає, що задавати покажчик на NULL завжди безглуздо. Наприклад, якщо p була змінною члена, що вказує на ресурс, який тривалість життя не був таким самим, як клас, що містить p, то встановлення p на NULL може бути корисним способом вказівки на наявність або відсутність ресурсу.


1
Я погоджуюся, що бувають випадки, коли це не допоможе, але ви, здається, натякаєте на те, що це може бути шкідливим. Це був ваш намір чи я неправильно прочитав?
Марк Викуп

1
Чи є копія вказівника, не має значення для питання, чи слід для змінної вказівника встановити значення NULL. Встановити його на NULL - це хороша практика на тих же підставах, що чищення посуду після того, як ви закінчили з вечерею, є хорошою практикою - хоча це не є захистом від усіх помилок, які може мати код, він сприяє гарному здоров'ю коду.
Франці Пенов

2
@Franci Багато людей, здається, не згодні з вами. І чи є копія, безумовно, актуально, якщо ви спробуєте використати копію після видалення оригіналу.

3
Франці, є різниця. Ви чистите посуд, оскільки використовуєте їх знову. Після видалення вказівника вам не потрібен. Це має бути останнє, що ти робиш. Краща практика - взагалі уникати ситуації.
GManNickG

1
Ви можете повторно використовувати змінну, але тоді це вже не стосується оборонного програмування; це те, як ви розробили вирішення проблеми. В ОП обговорюється, чи слід цього стилю оборони, до чого ми мусимо прагнути, а не ми коли-небудь встановимо покажчик на нуль. І в ідеалі, до вашого питання, так! Не використовуйте вказівники після їх видалення!
GManNickG

7

Якщо після цього буде більше коду delete, Так. Коли вказівник видаляється в конструкторі або в кінці методу чи функції, Ні.

Суть цієї притчі полягає в тому, щоб нагадати програмісту під час виконання, що об’єкт уже видалений.

Ще кращою практикою є використання розумних покажчиків (спільних або масштабних), які автоматично видаляють цільові об’єкти.


Усі (включаючи оригінального запитувача) погоджуються, що розумні покажчики - це шлях. Код розвивається. Після видалення може бути більше коду після видалення, але це, швидше за все, зміниться з часом. Введення завдання допомагає, коли це відбувається (і середній час майже нічого не коштує).
Адріан Маккарті

3

Як вже говорили інші, delete ptr; ptr = 0;демони не вилітають з вашого носа. Однак це все-таки заохочує використання ptrв якості прапора. Код засмічується deleteта встановлює покажчик на NULL. Наступним кроком є if (arg == NULL) return;розповсюдження коду для захисту від випадкового використання NULLвказівника. Проблема виникає після того, як перевірки NULLна предмет стануть вашим основним засобом перевірки стану об'єкта чи програми.

Я впевнений, що є запах коду щодо використання вказівника як прапора десь, але я його не знайшов.


9
Немає нічого поганого в тому, щоб використовувати вказівник як прапор. Якщо ви використовуєте вказівник і NULLне є дійсним значенням, вам, ймовірно, слід скористатися посиланням.
Адріан Маккарті

2

Я трохи зміню ваше запитання:

Чи використовуєте ви неініціалізований покажчик? Знаєте, той, на який ви не встановили NULL або виділили пам'ять, на яку він вказує?

Є два сценарії, коли встановлення вказівника на NULL можна пропустити:

  • змінна вказівник негайно виходить із сфери застосування
  • ви перевантажили семантику вказівника і використовуєте його значення не тільки як вказівник пам’яті, але і як ключове або неочищене значення. однак цей підхід страждає від інших проблем.

Тим часом, стверджуючи, що встановлення вказівника на NULL може приховати помилки для мене звучить як аргументація того, що ви не повинні виправляти помилку, оскільки виправлення може приховати ще одну помилку. Єдині помилки, які можуть відображатися, якщо вказівник не встановлений на NULL, будуть ті, які намагаються використовувати вказівник. Але встановлення його на NULL насправді призведе до точно такої ж помилки, як і при використанні звільненої пам'яті, чи не так?


(A) "звучить як аргументація того, що ви не повинні виправляти помилку" Не встановлення покажчика на NULL не є помилкою. (B) "Але встановлення його на NULL фактично призвело б до точно такої ж помилки". Ні. Встановлення NULL приховує подвійне видалення . (C) Підсумок: Встановлення NULL приховує подвійне видалення, але відкриває несвіжі посилання. Якщо не встановити NULL, можна приховати застарілі посилання, але викриває подвійні видалення. Обидві сторони погоджуються, що справжньою проблемою є виправлення несвіжих посилань та подвійне видалення.
Mooing Duck

2

Якщо у вас немає іншого обмеження, яке змушує вас або встановити або не встановити покажчик на NULL після видалення його (одне таке обмеження згадав Ніл Баттерворт ), тоді мої особисті переваги - це залишити його.

Для мене питання не в тому "це гарна ідея?" але "яку поведінку я б заважав чи дозволив досягти успіху, роблячи це?" Наприклад, якщо це дозволяє іншому коду бачити, що вказівник більше недоступний, чому інший код навіть намагається подивитися на звільнені вказівники після їх звільнення? Зазвичай це клоп.

Це також виконує більше роботи, ніж потрібно, а також перешкоджає налагодженню після смертей. Чим менше ви торкаєтесь пам’яті після того, як вона вам не потрібна, тим простіше з’ясувати, чому щось вийшло з ладу. Я багато разів покладався на те, що пам’ять знаходиться в подібному стані, коли коли-небудь помилка сталася для діагностики та виправлення зазначеної помилки.


2

Явне скасування нуля після видалення настійно підказує читачеві, що вказівник представляє щось, що концептуально є необов'язковим . Якби я побачив, що це робиться, я б почав турбуватися, що скрізь у джерелі вказівник звикне, що його слід спочатку перевірити на NULL.

Якщо це те, що ви насправді маєте на увазі, краще зробити це явне у джерелі, використовуючи щось на зразок boost :: необов’язковий

optional<Foo*> p (new Foo);
// (use p.get(), but must test p for truth first!...)
delete p.get();
p = optional<Foo*>();

Але якщо ви дійсно хотіли, щоб люди знали, що вказівник "пішов погано", я погоджусь на 100% згоду з тими, хто каже, що найкраще зробити - це змусити його вийти за межі сфери. Тоді ви використовуєте компілятор для запобігання можливості поганих відхилень під час виконання.

Це дитина у всій воді C ++, не повинна викидати її. :)


2

У добре структурованій програмі з відповідною перевіркою помилок немає причин не призначати її недійсною. 0окремо стоїть як загальновизнане недійсне значення в цьому контексті. Невдача і невдача.

Багато аргументів проти призначення 0припускають, що це може приховати помилку або ускладнити контрольний потік. По суті, це або помилка вгорі (не з вашої вини (вибачте за поганий каламбур)), або інша помилка від імені програміста - можливо, навіть ознака того, що потік програми став занадто складним.

Якщо програміст хоче ввести використання покажчика, який може бути нульовим як спеціальне значення, і записати всі необхідні ухилення навколо цього, це складне завдання, яке вони навмисно ввели. Чим кращий карантин, тим швидше ви виявите випадки нецільового використання, і тим менше вони зможуть поширюватися на інші програми.

Добре структуровані програми можуть бути розроблені за допомогою функцій C ++, щоб уникнути цих випадків. Ви можете використовувати посилання, або ви можете просто сказати "передача / використання нульових або недійсних аргументів - це помилка" - підхід, який однаково застосовний до контейнерів, таких як розумні покажчики. Посилення послідовної та правильної поведінки забороняє цим клопам добиратися далеко.

Звідси ви маєте лише обмежену область застосування та контекст, де може існувати (або дозволено) нульовий покажчик.

Те ж саме може бути застосовано до покажчиків, яких немає const. Наступне значення вказівника є тривіальним, оскільки його обсяг настільки малий, а неправильне використання перевірено та чітко визначено. Якщо ваш інструментарій та інженери не можуть слідувати за програмою після швидкого читання або є невідповідна перевірка помилок або непослідовний / поблажливий потік програми, у вас є інші, більші проблеми.

Нарешті, у вашому компіляторі та середовищі, ймовірно, є якісь запобіжники для тих часів, коли ви хочете ввести помилки (прошивання), виявити доступ до звільненої пам’яті та зловити інші пов'язані UB. Ви також можете вводити подібну діагностику у свої програми, часто не впливаючи на існуючі програми.


1

Дозвольте мені розкрити те, що ви вже поставили у своєму питанні.

Ось що ви поставили у своєму питанні у формі кулі:


Встановлення покажчиків на NULL після видалення не є універсальною хорошою практикою в C ++. Бувають випадки, коли:

  • це добре робити
  • і часи, коли це безглуздо і може приховати помилки.

Однак немає випадків, коли це погано ! Ви не будете вводити більше помилок, чітко скасуючи їх, ви не просочите пам’ять, не будете спричиняти не визначену поведінку .

Тож, якщо сумніваєтесь, просто обнуліть його.

Сказавши, що якщо ви відчуваєте, що вам потрібно явно обнулити якийсь вказівник, то для мене це звучить так, що ви недостатньо розділили метод, і слід переглянути підхід рефакторингу під назвою "Метод вилучення", щоб розділити метод на окремі частини.


Я не погоджуюся з "не буває випадків, коли це погано". Розглянемо кількість різаків, які ця ідіома вводить. У вас є заголовок, що міститься в кожному блоці, який щось видаляє, і всі ці місця видалення стають дещо менш прямими.
GManNickG

Бувають випадки, коли це погано. Якщо хтось спробує знеструмити ваш видалений зараз-null вказівник, коли він цього не зробив, він, ймовірно, не вийде з ладу, і ця помилка "прихована". Якщо вони відновлять ваш видалений покажчик, який все ще має деяке випадкове значення, ви, ймовірно, помітите, і помилку буде легше побачити.
Карсон Майєрс

@Carson: Мій досвід якраз протилежний: Перенаправлення нульптр майже всіма способами руйнує додаток і може бути спіймане налагоджувачем. Перенаправлення звисаючого покажчика зазвичай не створює проблем відразу, але часто призводить до неправильних результатів або інших помилок вниз по лінії.
MikeMB

@MikeMB Я повністю згоден, мої погляди на це суттєво змінилися за останні ~ 6,5 років
Карсон Майєрс

З точки зору того, що я був програмістом, ми всі були кимось ще 6-7 років тому :) Я навіть не впевнений, що наважився б сьогодні відповісти на питання C / C ++ :)
Lasse V. Karlsen

1

Так.

Єдина «шкода», яку вона може зробити - це ввести неефективність (непотрібна робота магазину) у вашу програму - але ця накладні витрати будуть незначними щодо витрат на виділення та звільнення блоку пам’яті у більшості випадків.

Якщо цього не зробити, одного дня у вас з’являться неприємні помилки щодо перенесення покажчика.

Я завжди використовую макрос для видалення:

#define SAFEDELETE(ptr) { delete(ptr); ptr = NULL; }

(і подібне для масиву, free (), випускаючи ручки)

Ви також можете записати методи самовидалення, які посилаються на покажчик коду виклику, тому вони змушують покажчик коду виклику на NULL. Наприклад, для видалення піддерева багатьох об'єктів:

static void TreeItem::DeleteSubtree(TreeItem *&rootObject)
{
    if (rootObject == NULL)
        return;

    rootObject->UnlinkFromParent();

    for (int i = 0; i < numChildren)
       DeleteSubtree(rootObject->child[i]);

    delete rootObject;
    rootObject = NULL;
}

редагувати

Так, ці методи порушують деякі правила щодо використання макросів (і так, в ці дні, можливо, ви могли б досягти такого ж результату за допомогою шаблонів) - але, використовуючи протягом багатьох років, я ніколи не звертався до мертвої пам’яті - однієї з найгучніших і найскладніших і найбільше часу для налагодження проблем, з якими ви можете зіткнутися. На практиці впродовж багатьох років вони ефективно усували класовий помилок кожної команди, в яку я їх представив.

Існує також багато способів, як ви могли реалізувати вищесказане - я просто намагаюся проілюструвати ідею змусити людей NULL вказівника, якщо вони видаляють об'єкт, а не надавати їм засоби для звільнення пам'яті, яка не NULL вказівника абонента. .

Звичайно, наведений вище приклад - це лише крок до автопоказника. Що я не запропонував, оскільки ОП спеціально розпитував про випадок не використання автоматичного вказівника.


2
Макроси - погана ідея, особливо коли вони виглядають як звичайні функції. Якщо ви хочете це зробити, використовуйте функцію шаблону.

2
Нічого собі ... Я ніколи не бачив нічого подібного, що anObject->Delete(anObject)скасовує anObjectпокажчик. Це просто лякає. Ви повинні створити для цього статичний метод, щоб змусити його робити TreeItem::Delete(anObject)як мінімум.
Д.Шоулі

Вибачте, введіть його як функцію, а не використовуючи відповідну верхню букву "це макрос". Виправлено. Також додав коментар, щоб пояснити себе краще.
Джейсон Вільямс

І ти маєш рацію, мій швидко розбитий приклад був сміттям! Виправлено :-). Я просто намагався придумати короткий приклад, щоб проілюструвати цю ідею: будь-який код, який видаляє вказівник, повинен гарантувати, що вказівник встановлений на NULL, навіть якщо хтось інший (абонент) володіє цим вказівником. Тому завжди передайте посилання на вказівник, щоб його можна було примусити до NULL в точці видалення.
Джейсон Вільямс

1

"Бувають випадки, коли це добре робити, і періоди, коли це безглуздо і може приховати помилки"

Я бачу дві проблеми: Цей простий код:

delete myObj;
myobj = 0

стає для вкладиша у багатопотокове середовище:

lock(myObjMutex); 
delete myObj;
myobj = 0
unlock(myObjMutex);

"Найкраща практика" Дон Нойфельда застосовується не завжди. Наприклад, в одному автомобільному проекті нам довелося встановити покажчики на 0 навіть у деструкторах. Я можу уявити, що в критичному для безпеки програмному забезпеченні такі правила не рідкість. Легше (і розумно) слідувати за ними, ніж намагатися переконати команду / перевірку коду для кожного використання вказівника у коді, що рядок, що скасовує цей покажчик, є зайвим.

Ще одна небезпека полягає у покладанні на цю техніку у використанні винятків:

try{  
   delete myObj; //exception in destructor
   myObj=0
}
catch
{
   //myObj=0; <- possibly resource-leak
}

if (myObj)
  // use myObj <--undefined behaviour

У такому коді ви створюєте витік ресурсів і відкладаєте проблему, або процес виходить з ладу.

Отже, ці дві проблеми, які мимоволі проходять через мою голову (Герб Саттер точно б розповів більше), ставлять для мене всі питання типу "Як уникнути використання смарт-покажчиків і виконувати роботу безпечно з нормальними покажчиками" як застарілі.


Я не бачу, як 4-лайнер значно складніший, ніж 3-лайнер (у будь-якому випадку слід використовувати lock_guards), і якщо ваш деструктор кидає, у вас все одно виникли проблеми.
MikeMB

Коли я вперше побачив цю відповідь, я не зрозумів, чому ви хочете обнулити покажчик у деструкторі, але зараз це роблю - це в тому випадку, коли об’єкт, що володіє вказівником, звикає після його видалення!
Марк Рансом

0

Завжди є покажчики, що турбуються.


1
Чи було б так важко поставити "звисаючі покажчики" замість "цього"? :) Принаймні, це дає вашій відповіді якусь субстанцію.
GManNickG

4
Це навіть тема підказки щодо якості W3C: "Не використовуйте" натисніть тут "як текст посилання": w3.org/QA/Tips/noClickHere
поза

0

Якщо ви збираєтесь перерозподілити вказівник перед тим, як використовувати його знову (вилучити його, передати його у функцію тощо), перетворення вказівника NULL - це лише додаткова операція. Однак якщо ви не впевнені, чи буде вона перерозподілена чи ні до її повторного використання, встановити її на NULL - це хороша ідея.

Як багато хто говорив, звичайно набагато простіше просто використовувати розумні покажчики.

Редагувати: Як сказав Томас Меттьюз у цій попередній відповіді , якщо покажчик видалений у деструкторі, немає необхідності присвоювати йому NULL, оскільки він більше не буде використаний, оскільки об’єкт вже знищується.


0

Я можу уявити встановлення покажчика на NULL після видалення його корисним у рідкісних випадках, коли існує законний сценарій повторного використання його в одній функції (або об'єкті). Інакше немає сенсу - покажчик повинен вказувати на щось значуще, поки воно існує - період.


0

Якщо код не належить до найважливішої частини вашої програми, будьте прості та використовуйте shared_ptr:

shared_ptr<Foo> p(new Foo);
//No more need to call delete

Він виконує підрахунок довідок і є безпечним для потоків. Ви можете знайти його в tr1 (std :: tr1 простір імен, #include <memory>) або якщо ваш компілятор не надає його, отримайте його з boost.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.