У C ++ скільки часу програміст витрачає на управління пам'яттю


39

Люди, які звикли до сміття зібрані мови, часто бояться управління пам’яттю C ++. Є такі інструменти, як auto_ptrі shared_ptrякі дозволять вирішити багато завдань управління пам’яттю. Безліч бібліотек C ++ передують цим інструментам і мають власний спосіб вирішення завдань управління пам'яттю.

Скільки часу ви витрачаєте на завдання управління пам’яттю?

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


1
Не так вже й багато, особливо ... Особливо з C ++ 0x, посиланнями та STL. Ви навіть можете писати код без управління пам'яттю.
Кодер

9
Загалом: Не так вже й багато, якщо ви досвідчені. Дуже багато, якщо ви не знаєте C ++ (-> зазвичай мисливські витоки пам'яті / ресурсів).
MaR

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

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

Як щодо "скільки часу використовується для налагодження керування пам'яттю, що зійшов з ладу?" По суті, керування пам’яттю можливе і не настільки складно в C ++. Факт: його налаштування - це точне ремесло, і воно дуже схильне до ебать. Коли ти трахаєшся, ти можеш навіть не помітити, а відстеження назад до старих помилок із нестабільною поведінкою, що накопичуються з часом, - це раковина в реальному часі, якої ти повинен боятися. Ось чому сучасні не зібрані сміття мови (я думаю про іржу) переклали велику відповідальність за перевірку типових помилок на компілятор.
ZJR

Відповіді:


54

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

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

Після того, як ви зрозумієте RAII , управління пам'яттю не буде проблемою.

Тоді, коли вам потрібно буде отримати доступ до сирої пам’яті, ви будете робити це в дуже конкретному, локалізованому та ідентифікованому коді, як у реалізаціях об’єктів пулу, а не «скрізь».

Поза таким кодом вам не потрібно буде маніпулювати пам'яттю, а лише життям об'єктів.

«Важка» частина - зрозуміти RAII.


10
Абсолютно вірно. В останні 5 років я писав лише "видалити", коли працював із застарілим кодом.
drxzcl

3
Я працюю у вбудованому середовищі із великим розміром стека. Настільки ж круто, як і RAII, він не спрацьовує добре, якщо місце у стеці є преміальним. Таким чином, це повернення до управління вказівниками.
Бастібе

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

12
@Paperflyer: RAII не займе більше місця в стеці, ніж deleteвручну, якщо у вас є одна реальна реалізація.
DeadMG

2
@Paperflyer: Розумний вказівник на купі займає той же простір; відмінність полягає в тому, що компілятор вставляє код розсилки ресурсів на всі виходи з функції. Оскільки це настільки широко використовується, як правило, це добре оптимізовано (наприклад, складання декількох виходів разом, якими ви не можете - ви не можете поставити код після a return)
MSalters

32

Управління пам’яттю використовується для відлякування дітей, але програміст повинен доглядати лише один вид ресурсів. Подумайте, як працювати з файлами, мережевими підключеннями та іншими ресурсами, які ви отримуєте з ОС.

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

Отже, коротко кажучи, я б припустив, що багато часу розробника C ++ витрачається на занепокоєння щодо управління пам'яттю. Як свідчить відповідь klaim , щойно ви отримуєте ручку на RAII, решта - це просто рефлекс.


3
Мені особливо подобається, як HttpWebRequest.GetResponse протікає з обробкою та починає виходити з мов GC. GC крутий, доки він не почне смоктати, оскільки ресурси все ще просочуються. msdn.microsoft.com/en-us/library/… Див. "Обережність".
Кодер

6
+1 для перегляду пам'яті як ресурсу. Спадковий код чи ні, скільки разів нам потрібно кричати: Управління пам'яттю - це навик, а не прокляття .
aquaherd

4
@Coder Не впевнений, чи дотримуюся я .. GC смокче, тому що в будь-якому випадку можна зловживати ресурсами ..? Я думаю, що C # добре справляється з детермінованим вивільненням ресурсів за допомогою IDisposable ...
Max

8
@Max: Тому що якщо це зібране сміття, я очікую не турбуватися про дурні ресурси через використання та користувальницькі IDisposables. Ресурси залишили сферу застосування, це все, їх слід очистити. Насправді, однак, я все ще мушу думати і здогадуватися, які з них витікатимуть, а які ні. Будь-який привід використовувати мову GC в першу чергу.
Кодер

5
@deadalnix У них є finalizeконструкція. Однак ви не знаєте, коли він буде викликаний. Чи буде раніше, ніж у вас закінчуються розетки або об’єкти WebResponse? Ви знайдете статтю, яка говорить про те, що не варто покладатися на це finalize- з поважною причиною.
Дайстер

13

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

Реальність полягає в тому, що спільне, унікальне і невласне для вас все постачається разом із вашим компілятором C ++ 11, і вам потрібно просто написати невеликі адаптери, щоб вони працювали навіть зі старим кодом.


1
Скільки навичок C ++ ви повинні мати, щоб: a) написати користувальницьку програму видалення; Я запитую, бо здається, що легко підібрати нову мову GC'd і наблизитись до правильності, не знаючи всього цього - чи легко також правильно потрапити в C ++?
Шон Макміллан

1
@SeanMcMillan: Користувацькі делетери є тривіальними для запису та розгортання, COM, про який я згадував, - це п'ять рядків для всіх типів COM, і кожен, хто має базову підготовку в сучасних C ++, повинен ознайомитися з ними. Ви не можете підібрати мову GCed, тому що сюрприз - GC не збиратиме COM-об’єкти. Або ручки файлів. Або пам'ять, отримана з інших систем. Або підключення до бази даних. RAII зробить усі ці речі.
DeadMG

2
Під "Підбери мову GC'd" я мав на увазі, що я перейшов між Java / C # / Ruby / Perl / Javascript / Python, і всі вони мають однаковий стиль управління ресурсами - пам'ять переважно автоматична, і все інше , вам доведеться керувати. Мені це здається, ніби ви говорите, що інструменти управління C ++ дозволяють вам керувати файловими файлами / db-з'єднаннями / тощо так само, як і пам'яттю, і що це досить просто, коли ви дізнаєтесь це. Не операція на мозку. Я правильно розумію?
Шон Макміллан

3
@SeanMcMillan: Так, саме так, і це не складно.
DeadMG

11

Коли я був програмістом на C ++ (давно), я довго переживав про помилку управління пам’яттю, намагаючись виправити важкі для відтворення помилок .

З модемом C ++ управління пам’яттю набагато менше проблем, але чи можете ви довіряти всім у великій команді, щоб виправити це. Яка вартість / час:

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

Тож це не просто час, який витрачаєш на « заробляння », це більше питання великих проектів.


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

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

1
так, це багато причин, чому більшість магазинів переїхали на Java або .NET. Збір сміття пом'якшує неминучу шкоду поганого коду.
Джеремі

1
Як не дивно, у нас немає цих проблем.
Девід Торнлі

1
@DavidThornley, я думаю, що багато проблем було з написанням коду інтерфейсу в C ++, в наші дні більшість кодів C ++, які я бачу, це не користувальницький інтерфейс
Ian

2

Я дуже багато використовую бібліотеки boost та TR1, і вони роблять управління пам’яттю у строгому розумінні (new / delete) непроблемним. З іншого боку, розподіл пам'яті в C ++ не з дешевих, і потрібно звернути увагу на те, де створені ці фантазійні загальні покажчики. Ви дуже часто використовуєте робочі простори або працюєте з пам'яттю на основі стека. Загалом, я б сказав, що це здебільшого проблема дизайну, а не проблема реалізації.


2

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

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

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

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

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

якщо ви не використовуєте контейнери (автоматичний / спільний покажчик), ви просто благаєте болю.

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


1

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

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

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

Тим часом, якщо я порівнюю, скажімо, з Java або C #, ви часто зустрічаєте людей, які мають закривати файли вручну там, відключати розетки вручну, встановлювати посилання об’єктів на null, щоб вони могли збирати сміття тощо. Є набагато більше пам'яті вручну і управління ресурсами на цих мовах, якщо ви запитаєте мене. У програмі C ++ вам навіть не потрібно unlockвручну вводити файли mutex, оскільки шафка mutex зробить це автоматично для вас, коли mutex вийде за межі області. Наприклад, вам ніколи не потрібно робити такі речі в C ++:

System.IO.StreamReader file = new System.IO.StreamReader(path);
try
{
    file.ReadBlock(buffer, index, buffer.Length);
}
catch (System.IO.IOException e)
{
    ...
}
finally
{
    if (file != null)
        file.Close();
}

Не потрібно робити такі речі, як закриття файлів вручну в C ++. Вони в кінцевому підсумку автоматично закриваються, щойно вони виходять із сфери дії, незалежно від того, виходять вони із сфери застосування в результаті або звичайних, або виняткових шляхів виконання. Подібна річ для ресурсів, пов’язаних із пам’яттю std::vector. Такий код, як file.Close()описано вище, часто нападає на користь, особливо в умовах finallyблоку, який передбачає, що локальний ресурс потрібно звільняти вручну, коли цілий настрій навколо C ++ повинен автоматизувати це.

Що стосується ручного управління пам'яттю, я б сказав, що C вимагає максимум, Java / C # середню кількість, а C ++ мінімум серед них. Існує багато причин бути трохи сором’язливим використання C ++, оскільки це освоєння дуже важкої мови, але управління пам'яттю не повинно бути однією з них. Навпаки, насправді я думаю, що це одна з найпростіших мов у цьому одному аспекті.

Звичайно, C ++ дозволяє вам розпочати розподіл пам'яті вручну та виклик operator delete/delete[]вручну вільної пам'яті. Він також дозволяє використовувати функції C, як mallocіfree. Але це практики кодування в давньому стилі, які, на мою думку, застаріли задовго до того, як люди дадуть кредит, оскільки Струструп виступав за RAII, перш ніж він навіть дуже рано ввів цей термін. Тому я навіть не думаю, що справедливо говорити, що "сучасний C ++" автоматизує управління ресурсами, тому що це повинно було бути цілою метою. Інакше ви не можете отримати безпеку винятків. Просто багато спокусливих розробників на початку 90-х намагалися використовувати C ++ так, як C з об'єктами, часто повністю ігноруючи обробку винятків, і це ніколи не слід було використовувати таким чином. Якщо ви використовуєте C ++ так, як це було практично завжди призначено для використання, то управління пам'яттю є повністю автоматизованим, і взагалі це не те, з чим вам доведеться вручну мати справу (або з якою слід мати справу).


1
Сучасна Java має "спробувати ресурси", яка видаляє весь цей брудний код, нарешті, блок. Рідко необхідно мати остаточний блок. Схоже, дизайнери скопіювали концепцію RAII.
ківірон

0

Залежить від старших технічних лідерів у колективі. У деяких компаніях (включаючи мою) не існує концепції, що називається розумним покажчиком. Вважається фантазією. Таким чином, люди просто кладуть видалення всюди і є накопичувач виправлення витоку пам'яті кожні 2 місяці. Нові хвилі операторів видалення надходять скрізь. Отже, залежить від компанії та типу людей, які там працюють.


1
Чи є у вашому оточенні щось, що заважає вам використовувати auto_ptrта друзів?
Шон Макміллан

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