В C ++ сколько времени программист тратит на управление памятью

39

Люди, которые привыкли собирать языки, часто боятся управления памятью в C ++. Существуют такие инструменты, как auto_ptrи shared_ptrкоторые будут выполнять многие задачи по управлению памятью для вас. Множество библиотек C ++ предшествуют этим инструментам и имеют собственный способ обработки задач управления памятью.

Сколько времени вы тратите на задачи управления памятью?

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

Шон Макмиллан
источник
1
Не так много, правда ... Особенно с C ++ 0x, ссылками и STL. Вы даже можете написать код без управления памятью вообще.
Кодер
9
В общем: не так уж много, если у вас есть опыт. Много, если вы новичок в C ++ (-> обычно охотятся за утечками памяти / ресурсов).
марта
1
Я нахожу реальный вопрос, в наши дни, больше о погоне за устаревшими ссылками. И это обычно довольно очевидно каждый раз, просто раздражает, что это не было поймано раньше: р
Матье М.
Я знаю, что это старый, но управление памятью IMO является неотъемлемой частью хорошего программиста. Абстракции, такие как контейнеры STL, хороши, но незнание памяти противоречит самой идее вычислений. Можно также спросить, как можно исключить алгебраические манипуляции, логику и циклы из арсенала программиста.
Ималлетт
Как насчет "сколько времени используется для отладки управления памятью?" По сути, управление памятью возможно и не так сложно в C ++. Факт в том, что его установка - это точное ремесло, и оно очень подвержено ошибкам. Когда вы облажаетесь, вы можете даже не заметить, и отслеживание старых ошибок с неустойчивым поведением, которое накапливалось с течением времени, - это реальное время, которое вы должны бояться. Вот почему современные языки без сборщика мусора (я думаю о ржавчине) перенесли большую ответственность за проверку типичных ошибок на компилятор.
ZJR

Ответы:

54

Современный C ++ заставляет вас не беспокоиться об управлении памятью до тех пор, пока вам не потребуется, то есть до тех пор, пока вам не понадобится организовать свою память вручную, в основном для целей оптимизации или если контекст заставляет вас это делать (подумайте об аппаратных средствах с большими ограничениями). Я писал целые игры, не манипулируя необработанной памятью, только опасаясь использования контейнеров, которые являются подходящим инструментом для работы, как на любом языке.

Так что это зависит от проекта, но большую часть времени вам приходится управлять не памятью, а только временем жизни объекта. Это решается с помощью умных указателей , которые являются одним из идиоматических инструментов C ++, полученных в результате RAII .

Как только вы поймете RAII , управление памятью не будет проблемой.

Затем, когда вам понадобится доступ к необработанной памяти, вы сделаете это в очень конкретном, локализованном и идентифицируемом коде, как в реализациях объектов пула, а не «везде».

Вне этого вида кода вам не нужно манипулировать памятью, только время жизни объектов.

Самое сложное - это понять RAII.

Klaim
источник
10
Абсолютная правда. За последние 5 лет я писал «delete» только при работе с устаревшим кодом.
drxzcl
3
Я работаю в ограниченной встроенной среде с большим размером стека. Как бы круто ни был RAII, он не работает хорошо, если пространство стека ограничено. Итак, вернемся к микроуправлению указателями.
бастиб
1
@nikie Я использую умные указатели библиотек в коде, которые управляют их API, затем я использую стандартные или улучшенные умные указатели в коде, специфичном для моего приложения (если я тот, кто решает об этом). Если вы можете изолировать библиотечный код в некоторых модулях, который абстрагируется от того, как они используются в вашем приложении, тогда вы избежите загрязнения API зависимостями.
Klaim
12
@Paperflyer: RAII не займет больше места в стеке, чем deleteвручную, если у вас нет одной хреновой реализации.
DeadMG
2
@Paperflyer: умный указатель на куче занимает то же место; Разница в том, что компилятор вставляет код освобождения ресурса во все выходы из функции. И так как это широко используется, это обычно хорошо оптимизировано (например, сложение нескольких выходов вместе таким образом, что вы не можете - вы не можете поместить код после a return)
MSalters
32

Управление памятью используется, чтобы напугать детей, но это всего лишь один из видов ресурсов, за которыми должен следить программист. Подумайте о дескрипторах файлов, сетевых подключениях, других ресурсах, которые вы получаете из ОС.

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

Итак, короче говоря, я бы предположил, что не так много времени разработчика C ++ тратится на заботу об управлении памятью. Как показывает ответ Клаима , как только вы получите дескриптор RAII, все остальное просто рефлекс.

Dysaster
источник
3
Мне особенно нравится, как HttpWebRequest.GetResponse утечки обрабатывает и начинает падать на языках GC. GC крут, пока не начнет сосать, потому что ресурсы все еще просачиваются. msdn.microsoft.com/en-us/library/… См. «Осторожно».
Кодер
6
+1 за просмотр памяти как ресурса. Устаревший код или нет, сколько раз нам нужно громко кричать: управление памятью - это навык, а не проклятие .
Аквахард
4
@ Кодер Не уверен, если я пойду .. GC отстой, потому что в любом случае возможно злоупотреблять ресурсами? Я думаю, что C # хорошо справляется с обеспечением детерминированного освобождения ресурсов, используя IDisposable ...
Макс
8
@ Макс: потому что, если это сборщик мусора, то я ожидаю, что не буду беспокоиться о глупых ресурсах с помощью пользовательских и IDisposables. Ресурсы вышли за рамки, вот и все, их надо почистить. В действительности, однако, я все еще должен думать и догадываться, какие из них будут течь, а какие нет. Превосходит любую причину использовать язык GC в первую очередь.
Кодер
5
@deadalnix У них есть finalizeконструкция. Однако вы не знаете, когда он будет вызван. Будет ли это до того, как у вас закончатся сокеты или объекты WebResponse? Вы найдете множество статей, которые говорят вам, что вы не должны полагаться finalize- и на то есть веская причина.
Dysaster
13

Почти нет. Даже в старых технологиях, таких как COM, вы можете написать собственные средства удаления для стандартных указателей, которые преобразуют их за очень короткое время. Например, std::unique_ptrможет быть преобразован в уникальную ссылку COM с пятью строками пользовательского удалителя. Даже если вам придется вручную писать собственный обработчик ресурсов, распространенность таких знаний, как SRP и копирование-и-своп, позволяет относительно легко написать класс управления ресурсами, который будет использоваться вечно.

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

DeadMG
источник
1
Сколько навыков в C ++ вам нужно, чтобы: а) написать собственный инструмент для удаления б) знать, что вам нужен специальный инструмент для удаления? Я спрашиваю, потому что кажется, что легко подобрать новый язык GC и получить почти корректный, не зная всего этого - легко ли получить право и в C ++?
Шон Макмиллан
1
@SeanMcMillan: Пользовательские средства удаления тривиальны для написания и развертывания. Упомянутый мной COM-компонент состоит из пяти строк для всех типов COM, и любой, кто имеет базовые знания по современному C ++, должен быть знаком с ними. Вы не можете выбрать язык GCed, потому что удивительно - GC не будет собирать COM-объекты. Или файловые ручки. Или память, полученная из других систем. Или соединения с базой данных. RAII сделает все эти вещи.
DeadMG
2
«Подбери язык GC» я имел в виду, что я переключился между Java / C # / Ruby / Perl / Javascript / Python, и все они имеют одинаковый стиль управления ресурсами - память в основном автоматическая, и все остальное , ты должен управлять. Мне кажется, что вы говорите, что инструменты управления C ++ позволяют вам управлять дескрипторами файлов / соединениями db / etc так же, как с памятью, и что это относительно просто, как только вы изучите это. Не мозговая операция. Я правильно понимаю?
Шон Макмиллан
3
@SeanMcMillan: Да, это точно, и это не сложно.
DeadMG
11

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

С модемом C ++ управление памятью становится намного менее сложной задачей, но вы можете довериться всем в большой команде, чтобы сделать это правильно. Какова стоимость / время:

  • Обучение (не многие программисты хорошо разбираются в проблемах)
  • Обзоры кода для поиска проблем с управлением памятью
  • Отладка проблем управления памятью
  • Всегда нужно помнить, что ошибка в одной части приложения может быть связана с проблемой управления памятью в несвязанной части приложения .

Так что это не просто время, затрачиваемое на « выполнение », это больше проблема крупных проектов.

Ян
источник
2
Я думаю, что некоторые C ++ проекты отчаянно пытались исправить некоторые утечки памяти из-за плохо написанного кода. Плохой код может случиться, и когда это произойдет, это может отнять много времени у других людей.
Джереми
@ Джереми, я обнаружил, что когда я перешел с C ++ на C #, там было столько же плохо написанного кода (если не больше), но, по крайней мере, было очень легко найти ту часть программы, которая имела данную ошибку.
Ян
1
да, именно поэтому большинство магазинов перешли на Java или .NET. Сборка мусора уменьшает неизбежный ущерб от плохого кода.
Джереми
1
Как ни странно, у нас нет таких проблем.
Дэвид Торнли
1
@DavidThornley, я думаю, что большая часть проблемы заключалась в написании кода пользовательского интерфейса на C ++, в наши дни большая часть кода C ++, который я вижу, не является пользовательским интерфейсом
Ян
2

Я часто использую библиотеки boost и TR1, и они делают управление памятью в строгом смысле слова (new / delete) несложным. С другой стороны, выделение памяти в C ++ недешево, и нужно обратить внимание на то, где создаются эти причудливые общие указатели. Вы часто используете рабочие пространства или работаете со стековой памятью. В целом, я бы сказал, что это в основном проблема дизайна, а не проблема реализации.

quant_dev
источник
2

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

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

после этого вам нужно будет учитывать память и владение. в системе более низкого уровня это хорошо и необходимо, но может потребоваться некоторое время и леса, чтобы реализовать то, как вы должны перемещать вещи. я не вижу в этом боли, так как это требование системы более низкого уровня. собственность, контроль и ответственность очевидны.

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

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

если вы не используете контейнеры (автоматический / общий указатель), то вы просто умоляете о боли.

Я реализовал свои собственные библиотеки. Мне нужно время, чтобы реализовать эти вещи, но большинство людей используют их повторно (что обычно является хорошей идеей).

джастин
источник
1

Ты имеешь в виду, что нужно вручную освобождать память, закрывать файлы и тому подобное? Если это так, я бы сказал, минимум и, как правило, меньше, чем большинство других языков, которые я использовал, особенно если мы обобщим это не только для «управления памятью», но и «управления ресурсами». В этом смысле я на самом деле думаю, что C ++ требует меньше ручного управления ресурсами, чем, скажем, Java или C #.

Это происходит главным образом из-за деструкторов, которые автоматизируют уничтожение ресурса (памяти или иным образом). Как правило, единственный раз, когда мне нужно освободить / уничтожить ресурс вручную в C ++, это если я реализую структуру данных vlow-уровня (что большинству людей не нужно делать) или использую C API, где я просто провожу немного времени обертывание ресурса C, который необходимо вручную освободить / уничтожить / закрыть, в оболочку C ++, соответствующую RAII.

Конечно, если пользователь просит закрыть изображение в программном обеспечении для редактирования изображений, я должен удалить изображение из коллекции или что-то еще. Но, надеюсь, это не считается как управление «памятью» или «ресурсами», которое имеет значение в этом контексте, поскольку это требуется в любом языке, если вы хотите освободить память, связанную с этим образом в то время. Но опять же, все, что вам нужно сделать, это удалить изображение из коллекции, а деструктор изображения позаботится обо всем остальном.

Между тем, если сравнивать, скажем, с Java или C #, вы часто обнаруживаете, что людям приходится закрывать там файлы вручную, вручную отключать сокеты, устанавливать нулевые ссылки на объекты, чтобы их можно было собирать мусором, и т. Д. управление ресурсами на этих языках, если вы спросите меня. В C ++ вам часто даже не нужен unlockмьютекс вручную, так как мьютекс-локатор сделает это автоматически, когда мьютекс выйдет из области видимости. Например, вы никогда не должны делать такие вещи в 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 имеет «попробовать с ресурсами», которая удаляет весь этот грязный код в блоке finally. Редко необходимо иметь окончательный блок. Похоже, дизайнеры скопировали концепцию RAII.
Кивирон
0

Зависит от старшего технического руководства в команде. В некоторых компаниях (включая мою) нет понятия «умный указатель». Это считается фантазией. Таким образом, люди просто помещают удаления повсюду, и каждые 2 месяца существует система устранения утечек памяти. Новая волна утверждений удаления прибывает всюду. Итак, зависит от компании и типа людей, которые там работают.

Джаганнатха
источник
1
Есть ли что-то в вашей среде, что мешает вам использовать auto_ptrи друзей?
Шон Макмиллан
2
Похоже, ваша компания не пишет код на C ++, вы пишете C.
gbjbaanb