Загальні вказівки щодо уникнення витоків пам'яті в C ++ [закрито]


130

Назвіть кілька загальних порад, щоб переконатися, що я не просочуюся пам’яттю в програмах C ++? Як визначити, хто повинен звільнити динамічно розподілену пам'ять?


26
Мені це здається досить конструктивним.
взуття

11
Це конструктивно. А відповіді підтверджуються фактами, експертизою, посиланнями тощо. І дивіться кількість оновлень / відповідей .. !!
Саміта Чатхуранга

Відповіді:


40

Замість того, щоб керувати пам’яттю вручну, спробуйте використовувати розумні покажчики, де це можливо.
Погляньте на лівінг Boost , TR1 та смарт-покажчики .
Також розумні покажчики тепер є частиною стандарту C ++, який називається C ++ 11 .


1
Для компіляції за допомогою g ++ потрібно додати парам: -std = c ++ 0x
Paweł Szczur

або ви можете скласти з g ++, використовуючи значення прапора -std = c ++ 11
Prabhash Rathore

200

Я ретельно схвалюю всі поради щодо RAII та розумних покажчиків, але я також хотів би додати трохи підказку більш високого рівня: найпростіша пам'ять для управління - це пам'ять, яку ви ніколи не виділяли. На відміну від таких мов, як C # та Java, де майже все є посиланням, у C ++ ви повинні ставити об'єкти в стек, коли зможете. Як я бачу, декілька людей (включаючи доктора Струструпа) зазначають, що основна причина, по якій збирання сміття ніколи не користувалася популярністю у С ++, полягає в тому, що добре написаний C ++ не виробляє багато сміття.

Не пишіть

Object* x = new Object;

або навіть

shared_ptr<Object> x(new Object);

коли ти можеш просто писати

Object x;

34
Я б хотів, щоб я міг дати це +10. Це найбільша проблема, яку я бачу з більшістю програмістів на C ++ сьогодні, і я припускаю, що це тому, що вони вивчили Java до C ++.
Крістофер Джонсон

Дуже цікавий момент - я задумався, чому у мене проблеми з керуванням пам’яттю C ++ набагато рідше, ніж на інших мовах, але тепер я розумію, чому: насправді це дозволяє виходити на стек, як у ванілі C.
ArtOfWarfare

Отже, що ви робите, якщо пишете Object x; а потім хочете викинути х? Скажімо, x створено в основному методі.
Ямча

3
@ user1316459 C ++ дозволяє також створювати сфери дії на льоту. Все, що вам потрібно зробити, - це обернути термін служби x в такі дужки: {Object x; x.DoSomething; }. Після остаточного '}' деструктора x буде викликано звільнення будь-яких ресурсів, які він містить. Якщо x - сама пам'ять, яка повинна виділятися на купі, я пропоную загортати її в унікальний_ptr, щоб вона легко і належним чином очищалася.
Девід Петерсон

1
Роберт: так. Росс не сказав "Ніколи не пишіть [код, що містить новий]", він сказав "Не пишіть [те], коли ви можете просто [поставити його на стек]". Великі об'єкти в купі залишатимуться правильним дзвінком у більшості ситуацій, особливо для коду, що підвищує продуктивність.
кодетаку

104

Використовуйте RAII

  • Забудьте про збирання сміття (замість цього використовуйте RAII). Зауважте, що навіть сміттєзбірник теж може просочитися (якщо ви забудете "звести нанівець" деякі посилання в Java / C #), і що смітник не допоможе вам розпоряджатися ресурсами (якщо у вас є об'єкт, який придбав ручку для файл, файл не буде звільнений автоматично, коли об’єкт вийде із сфери застосування, якщо ви не зробите це вручну на Java або використовуєте шаблон "розпорядження" в C #).
  • Забудьте про правило "одне повернення на функцію" . Це хороша порада C, щоб уникнути протікань, але вона застаріла в C ++ через використання виключень (замість цього використовуйте RAII).
  • І хоча "Сендвіч-шаблон" є гарною порадою C, він застарілий у C ++ через його винятки (замість цього використовуйте RAII).

Ця публікація здається повторюваною, але для C ++ найосновнішою моделлю, яку потрібно знати, є RAII .

Навчіться використовувати розумні покажчики як від boost, TR1 або навіть низького (але часто досить ефективного) auto_ptr (але ви повинні знати його обмеження).

RAII є основою як безпеки винятків, так і видалення ресурсів у C ++, і жоден інший зразок (сендвіч тощо) не дасть вам обох (і більшість часу він не дасть вам жодного).

Нижче див. Порівняння RAII та не RAII-коду:

void doSandwich()
{
   T * p = new T() ;
   // do something with p
   delete p ; // leak if the p processing throws or return
}

void doRAIIDynamic()
{
   std::auto_ptr<T> p(new T()) ; // you can use other smart pointers, too
   // do something with p
   // WON'T EVER LEAK, even in case of exceptions, returns, breaks, etc.
}

void doRAIIStatic()
{
   T p ;
   // do something with p
   // WON'T EVER LEAK, even in case of exceptions, returns, breaks, etc.
}

Про RAII

Підводячи підсумок (після коментаря Огре Псалма33 ), RAII спирається на три поняття:

  • Після того як об’єкт побудований, він просто працює! Набувати ресурсів у конструктора.
  • Руйнування об’єктів достатньо! Вільні ресурси в деструкторі.
  • Це все про сфери застосування! Об'єкти, що охоплюють область (див. DoRAIIStatic приклад вище) будуть побудовані при їх оголошенні та будуть знищені в момент, коли виконання вийде з області застосування, незалежно від способу виходу (повернення, перерва, виняток тощо).

Це означає, що в правильному коді C ++ більшість об'єктів не буде побудовано з new, а буде оголошено на стеці. А для тих, хто побудований за допомогою new, все буде якось розмальовано (наприклад, прикріплене до розумного вказівника).

Як розробник, це дуже потужно, тому що вам не потрібно буде дбати про ручну обробку ресурсів (як це робиться на C, або про деякі об'єкти на Java, які інтенсивно використовують try/ finallyдля цього випадку) ...

Редагувати (2012-02-12)

"обшиті об'єкти ... будуть знищені ... незалежно від виходу", це не зовсім вірно. є способи обдурити RAII. будь-який аромат терміналу () обійде очищення. вихід (EXIT_SUCCESS) є оксимороном у цьому плані.

- wilhelmtell

wilhelmtell цілком прав щодо цього: Існують виняткові способи обдурити RAII, що призводить до різкої зупинки процесу.

Це виняткові способи, оскільки код C ++ не засмічений терміном завершення, виходу тощо, або, у випадку, якщо винятки, ми хочемо, щоб необроблений виняток збивав процес і ядро ​​скидало його пам'ять, як є, а не після очищення.

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

(хто телефонує terminateабо exitу випадковому коді C ++? ... Пам’ятаю, що довелося вирішувати цю проблему під час гри з GLUT : Ця бібліотека дуже орієнтована на С, підходить до того, як активно розробляти її, щоб ускладнити розробникам C ++ такі речі, як не дбайливі. про стек розподілених даних або про "цікаві" рішення про те, щоб ніколи не повертатися зі свого основного циклу ... я не буду коментувати це) .


Чи не повинен T клас використовувати RAII, щоб бути впевненим, що doRAIIStatic () не протікає пам'ять? Наприклад T p (); p.doSandwich (); Я насправді про це не знаю багато.
Даніель О

@Ogre Psalm33: Дякую за коментар. Звичайно, ти маєш рацію. Я додав як посилання на сторінку Вікіпедії RAII, так і невеликий підсумок того, що таке RAII.
paercebal

1
@Shiftbit: Три способи, в порядку переваги: ​​_ _ _ 1. Покладіть реальний предмет всередину контейнера STL. _ _ _ 2. Помістіть інтелектуальні покажчики (shared_ptr) об’єктів всередині контейнера STL. _ _ _ 3. Помістіть необроблені покажчики всередині контейнера STL, але загорнуйте контейнер, щоб контролювати будь-який доступ до даних. Обгортка переконається, що деструктор звільнить виділені об'єкти, а аксесуари для обгортки переконаються, що нічого не порушено під час доступу / зміни контейнера.
paercebal

1
@Robert: У C ++ 03 ви б використовували doRAIIDynamic у функції, яка повинна надати право власності на дочірню або батьківську функцію (або глобальну область). Або коли ви отримуєте інтерфейс до поліморфного об'єкта через фабрику (повертаючи розумний вказівник, якщо він правильно написаний). У C ++ 11 це менше випадку, тому що ви можете зробити свій об’єкт рухомим, тому надати право власності на об’єкт, оголошений у стеці, простіше ...
paercebal

2
@Robert: ... Зауважте, що оголошення об’єкта в стеку не означає, що об'єкт не використовує купу внутрішньо (зверніть увагу на подвійне заперечення ... :-) ...). Наприклад, std :: string, реалізований з оптимізацією малих рядків, матиме буфер "на стеці класу" для невеликих рядків (~ 15 символів), і буде використовувати вказівник на пам'ять у купі для більших рядків ... Але ззовні std :: string - це все-таки тип значення, який ви декларуєте (як правило) на стеці і використовуєте так, як використовували б ціле число (на відміну від: як ви б використовували інтерфейс для класу поліморфів).
paercebal

25

Ви хочете подивитися на розумні покажчики, такі як розумні вказівники boost .

Замість

int main()
{ 
    Object* obj = new Object();
    //...
    delete obj;
}

boost :: shared_ptr автоматично видаляється, коли кількість посилань дорівнює нулю:

int main()
{
    boost::shared_ptr<Object> obj(new Object());
    //...
    // destructor destroys when reference count is zero
}

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

Однак це не панацея. Хоча ви можете отримати доступ до базового вказівника, ви не хочете передати його сторонній API, якщо тільки ви не були впевнені в тому, що він робить. Багато разів ваше "опублікування" матеріалів на якусь іншу нитку для роботи, яку слід виконати ПІСЛЯ створення завершеного створення. Це звичайно для PostThreadMessage в Win32:

void foo()
{
   boost::shared_ptr<Object> obj(new Object()); 

   // Simplified here
   PostThreadMessage(...., (LPARAM)ob.get());
   // Destructor destroys! pointer sent to PostThreadMessage is invalid! Zohnoes!
}

Як завжди, використовуйте свою шапку для мислення будь-яким інструментом ...



11

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

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

Якщо вам потрібно "новий" об'єкт, то більшу частину часу він буде мати єдиного очевидного власника протягом усього свого життя. У цій ситуації я схильний використовувати купу шаблонів колекцій, призначених для "володіння" об'єктами, що зберігаються в них за допомогою вказівника. Вони реалізовані з контейнерами вектора STL та картою, але мають деякі відмінності:

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

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


10

Ба, ви, маленькі діти, і ваші новомодні збирачі сміття ...

Дуже чіткі правила щодо "права власності" - який об’єкт чи частина програмного забезпечення має право видалити об'єкт. Очистіть коментарі та мудрі імена змінних, щоб стало очевидним, якщо вказівник "володіє" чи "просто дивіться, не торкайтеся". Щоб вирішити, кому належить, дотримуйтесь якомога більше шаблону "сендвіч" у межах кожної підпрограми чи методу.

create a thing
use that thing
destroy that thing

Іноді доводиться створювати і руйнувати в абсолютно різних місцях; я думаю важко цього уникнути.

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

Багато інших покажчиків визначаються за потребою кожного разу, коли одному суб'єкту потрібен доступ до іншого, для сканування арайів чи будь-чого іншого; це "просто дивлячись". Для прикладу 3D-сцени - об’єкт використовує текстуру, але не володіє; інші об'єкти можуть використовувати ту саму текстуру. Знищення об'єкта не викликає руйнування будь-яких текстур.

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


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

6
Я не погоджуюсь. у частині "використати цю річ", якщо повернення чи виняток буде кинуто, ви пропустите угоду. Щодо продуктивності, то std :: auto_ptr нічого не коштуватиме вам. Не те, що я ніколи не кодую так, як ви. Просто різниця між 100% і 99% захищеним кодом. :-)
paercebal

8

Чудове запитання!

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

Я думаю, що краще, що ти можеш зробити, це об’єднати цікаві твори різних авторів, я можу дати вам підказку:

  • Розподільник фіксованого розміру сильно обговорюється скрізь у мережі

  • Виділення малих об'єктів було введено Олександреску в 2001 році в його ідеальній книзі "Сучасний дизайн c ++"

  • Великий прогрес (з розповсюдженим вихідним кодом) можна знайти у дивовижній статті в програмуванні ігор Gem 7 (2008) під назвою "Високопродуктивний розподільник купи", написаному Димитром Лазаровим

  • Чудовий перелік ресурсів можна знайти в цій статті

Не починайте писати нооб непотрібний розподільник самостійно ... ДОКУМЕНТАЙТЕ СЕБЕ.


5

Однією з прийомів, яка стала популярною в управлінні пам'яттю в C ++, є RAII . В основному ви використовуєте конструктори / деструктори для обробки розподілу ресурсів. Звичайно, є деякі інші неприємні деталі в C ++ через безпеку винятків, але основна ідея досить проста.

Питання, як правило, зводиться до права власності. Я настійно рекомендую прочитати серію "Ефективні C ++" Скотта Майєрса та "Сучасний дизайн C ++" Андрія Олександреску.


5

Про те, як не протікати, вже багато, але якщо вам потрібен інструмент, який допоможе вам відстежувати витоки, подивіться:


BoundsChecker - 404ing.
TankorSmash

4

Інтелектуальні вказівники користувача скрізь, де ви можете! Цілі класи витоку пам'яті просто проходять.


4

Діліться і знайте правила власності на пам'ять у вашому проекті. Використання правил COM забезпечує найкращу узгодженість (параметри [in] належать абоненту, callee повинен скопіювати; [out] params належать абоненту, callee повинен зробити копію, якщо зберігає посилання; тощо)


4

valgrind - це хороший інструмент для перевірки витоків пам’яті ваших програм також під час виконання.

Він доступний для більшості ароматів Linux (включаючи Android) та Darwin.

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

Звичайно, ця порада залишається дійсною для будь-якого іншого інструменту перевірки пам'яті.


3

Крім того, не використовуйте виділену вручну пам'ять, якщо є клас бібліотеки std (наприклад, вектор). Переконайтесь, що ви порушуєте це правило, що у вас є віртуальний деструктор.


2

Якщо ви не можете / не використовуєте смарт-вказівник для чогось (хоча це повинен бути величезний червоний прапор), введіть свій код за допомогою:

allocate
if allocation succeeded:
{ //scope)
     deallocate()
}

Це очевидно, але переконайтеся, що ви вводите його, перш ніж вводити будь-який код у цій області


2

Частим джерелом цих помилок є те, коли у вас є метод, який приймає посилання або вказівник на об’єкт, але залишає право власності незрозумілим. Стилі та умови коментування можуть зробити це менш імовірним.

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

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

Не використовуйте в списках аргументів non-const посилання. Під час читання коду, що викликає, дуже незрозуміло, що користувач може зберігати посилання на параметр.

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


2

Поради щодо важливості:

-Порада №1. Не забудьте оголосити ваших деструкторів "віртуальними".

-Порада №2 Використовуйте RAII

-Порада №3 Використовуйте smartpointers boost

-Скада №4 Не пишіть власні баггі Smartpointers, використовуйте boost (у проекті, який я зараз працюю, я не можу використовувати boost, і мені довелося налагоджувати власні розумні покажчики, я б точно не брав знову той самий маршрут, але потім знову зараз, я не можу додати підсилення до наших залежностей)

-Порада №5. Якщо деякі випадкові / неефективні критичні (як в іграх з тисячами об'єктів) роботи, подивіться на контейнер підвищення покажчика Thorsten Ottosen

-Порада №6. Знайдіть заголовок виявлення витоку для вашої платформи, наприклад, заголовок "vld" Visual Deakction


Можливо, мені не вистачає хитрості, але як "гра" та "критично неефективні" можуть бути в одному реченні?
Адам Нейлор

Ігри - це приклад критичного сценарію, звичайно. Можливо, там не вдалося зрозуміти
Роберт Гулд

Порада №1 повинна застосовуватися лише в тому випадку, якщо в класі є хоча б один віртуальний метод. Я ніколи не нав'язував би марний віртуальний деструктор класу, який не повинен слугувати базовим класом у поліморфному дереві спадкування.
антред

1

Якщо можете, використовуйте boost shared_ptr та стандартний C ++ auto_ptr. Вони передають семантику власності.

Коли ви повертаєте auto_ptr, ви повідомляєте абоненту, що ви надаєте їм право власності на пам'ять.

Коли ви повертаєте shared_ptr, ви повідомляєте абоненту, що у вас є посилання на нього, і він бере частину права власності, але це не лише їхня відповідальність.

Ця семантика також стосується параметрів. Якщо абонент передає вам auto_ptr, він надає вам право власності.


1

Інші згадали про способи уникнення витоку пам’яті в першу чергу (наприклад, розумні покажчики). Але інструмент профілювання та аналізу пам’яті часто є єдиним способом відстежувати проблеми з пам’яттю після їх виникнення.

Valgrind memcheck - відмінний безкоштовний.


1

Лише для MSVC додайте наступне у верхній частині кожного .cpp-файлу:

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

Потім, під час налагодження з VS2003 або новішою версією, вам повідомлять про будь-які витоки, коли програма закінчується (вона відстежує нову / видалену). Це є основним, але це допомагало мені в минулому.



1

Якщо ви збираєтеся керувати пам’яттю вручну, у вас є два випадки:

  1. Я створив об’єкт (можливо, опосередковано, викликавши функцію, яка виділяє новий об’єкт), використовую його (або функцію, яку я називаю, використовує), потім звільняю її.
  2. Хтось дав мені довідку, тому я не повинен її звільняти.

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

Мова йде про право власності на вказівник.


1
  • Намагайтеся уникати динамічного розподілу об'єктів. Поки в класах є відповідні конструктори та деструктори, використовуйте змінну типу класу, а не вказівник на неї, і ви уникаєте динамічного розподілу та делокації, оскільки компілятор зробить це за вас.
    Насправді це також механізм, який використовуються "розумні покажчики", а деякі інші автори називають RAII ;-).
  • Передаючи об'єкти іншим функціям, віддайте перевагу еталонним параметрам над покажчиками. Це дозволяє уникнути деяких можливих помилок.
  • Декларуйте параметри const, де це можливо, особливо вказівники на об’єкти. Таким чином, об'єкти не можуть бути звільнені "прискіпливо" (за винятком випадків, коли ви відкидаєте const ;-))).
  • Мінімізуйте кількість місць у програмі, де ви здійснюєте розподіл пам’яті та розсилку. E. g. якщо ви виділяєте або звільняєте один і той же тип кілька разів, напишіть для нього функцію (або заводський метод ;-)).
    Таким чином можна легко створити вихід налагодження (які адреси розподіляються та розміщуються, ...) легко, якщо потрібно.
  • Використовуйте заводську функцію, щоб виділити об'єкти кількох споріднених класів з однієї функції.
  • Якщо ваші класи мають загальний базовий клас з віртуальним деструктором, ви можете звільнити їх усіх за допомогою тієї ж функції (або статичного методу).
  • Перевірте свою програму інструментами типу очищення (на жаль, багато $ / € / ...).

0

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

Це також можна зробити під час компіляції, замінивши операторів на нові та видалити та інші функції розподілу пам'яті.

Наприклад, перевірте на цьому веб-сайті [Налагодження розподілу пам'яті в C ++] Примітка. Існує хитрість для оператора видалення також приблизно так:

#define DEBUG_DELETE PrepareDelete(__LINE__,__FILE__); delete
#define delete DEBUG_DELETE

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

Ви також можете спробувати щось на зразок BoundsChecker під Visual Studio, що досить цікаво і просто у використанні.


0

Ми обертаємо всі наші функції розподілу шаром, який додає коротку рядок на передній частині та дозорний прапор в кінці. Так, наприклад, у вас буде виклик "myalloc (pszSomeString, iSize, iAlignment); або новий (" опис ", iSize) MyObject (); який внутрішньо виділяє вказаний розмір плюс достатньо місця для вашого заголовка та довідкової служби. Звичайно , не забудьте прокоментувати це для побудови без налагодження! Для цього потрібно трохи більше пам’яті, але переваги значно перевищують витрати.

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


0

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


0

Один з єдиних прикладів розподілу та знищення в різних місцях - це створення ниток (параметр, який ви передаєте). Але навіть у цьому випадку це легко. Ось функція / метод створення потоку:

struct myparams {
int x;
std::vector<double> z;
}

std::auto_ptr<myparams> param(new myparams(x, ...));
// Release the ownership in case thread creation is successfull
if (0 == pthread_create(&th, NULL, th_func, param.get()) param.release();
...

Тут замість цього функція потоку

extern "C" void* th_func(void* p) {
   try {
       std::auto_ptr<myparams> param((myparams*)p);
       ...
   } catch(...) {
   }
   return 0;
}

Досить easyn чи не так? У разі невдалого створення потоку ресурс буде автоматично вилучений (видалений) auto_ptr, інакше право власності буде передано потоці. Що робити, якщо потік настільки швидкий, що після створення він звільняє ресурс перед

param.release();

називається в основній функції / методі? Нічого! Тому що ми «скажемо» auto_ptr ігнорувати угоду. Чи легко управління пам'яттю C ++, чи не так? Ура,

Ема!


0

Керуйте пам'яттю так само, як ви керуєте іншими ресурсами (ручками, файлами, db-підключеннями, сокетами ...). GC теж не допоможе вам з ними.


-3

Рівно одне повернення з будь-якої функції. Таким чином, ви можете там розібратися і ніколи цього не пропустити.

Інакше легко помилитися:

new a()
if (Bad()) {delete a; return;}
new b()
if (Bad()) {delete a; delete b; return;}
... // etc.

Ваша відповідь не відповідає прикладу коду тут? Я погоджуюся з відповіддю "лише одне повернення", але приклад коду показує, що НЕ робити.
simon

1
Точка C ++ RAII полягає саме в тому, щоб уникнути виду коду, який ви написали. У С, це, мабуть, правильно. Але в C ++ ваш код недосконалий. Наприклад: Що робити, якщо новий b () кидає? Ви протікаєте.
paercebal
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.