Управление памятью С++

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

В настоящее время я не могу использовать boost. Я должен придерживаться чистого С++, потому что используемая среда запрещает использование дженериков.

Ответ 1

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

  • Только один объект будет иметь указатель. По умолчанию это создатель.
  • Собственность может быть передана. Чтобы указать передачу права собственности, объект передается как указатель в сигнатуре метода (например, void Foo (Bar * zonk);).
  • Владелец решит, когда удалить объект.
  • Чтобы передать объект методу, предназначенному только для использования, объект передается как ссылка в сигнатуре метода (например, void Foo (Bat & zonk);).
  • Классы, не принадлежащие владельцам, могут хранить ссылки (никогда не указатели) на объекты, которые они заданы, только когда они могут быть уверены, что владелец не будет уничтожать его во время использования.

В принципе, если класс просто использует что-то, он использует ссылку. Если класс владеет чем-то, он использует указатель.

Это работало красиво и было приятно использовать. Проблемы с памятью были очень редки.

Ответ 2

Правила:

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

Ответ 3

Я бы добавил еще одно правило:

  • Не создавайте и не удаляйте объект, когда автоматический объект будет работать нормально.

Мы обнаружили, что программисты, которые не знакомы с С++ или программисты, переходящие с таких языков, как Java, похоже, узнают о новых, а затем одержимо используют его всякий раз, когда хотят создать любой объект, независимо от контекста. Это особенно пагубно, когда объект создается локально внутри функции, чтобы делать что-то полезное. Использование нового таким образом может нанести ущерб производительности и может слишком легко ввести глупые утечки памяти, когда соответствующее удаление будет забыто. Да, умные указатели могут помочь с последним, но это не решит проблемы с производительностью (предполагая, что за кулисами используется новый/удалить или эквивалент). Интересно (ну, может быть), мы обнаружили, что при использовании Visual С++ удаление часто бывает дороже нового.

Некоторые из этих путаниц также возникают из-за того, что функции, которые они называют, могут принимать указатели или даже интеллектуальные указатели в качестве аргументов (когда ссылки, возможно, были бы лучше/понятнее). Это заставляет их думать, что им нужно "создать" указатель (многие думают, что это то, что нового делает), чтобы передать указатель на функцию. Ясно, что для этого требуются некоторые правила о том, как написаны API-интерфейсы, чтобы сделать соглашения о вызовах как можно более однозначными, что подкреплено четкими комментариями, предоставленными прототипом функции.

Ответ 4

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

Ответ 5

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

Иногда вам нужно выделять объекты динамически, но они будут использоваться только в течение определенного промежутка времени. Например, в предыдущем проекте мне нужно было создать сложное представление в памяти схемы базы данных - в основном сложный циклический график объектов. Тем не менее, график нужен только для продолжительности соединения с базой данных, после чего все узлы могут быть освобождены одним выстрелом. В этом сценарии хороший шаблон для использования - это то, что я называю "локальным GC-идиомой". Я не уверен, имеет ли это "официальное" имя, поскольку это то, что я видел только в своем собственном коде, и в Cocoa (см. NSAutoreleasePool в справочнике Apple Cocoa).

Вкратце, вы создаете объект "collector", который хранит указатели на временные объекты, которые вы выделяете с помощью нового. Обычно это привязано к определенной области вашей программы, либо статической области (например, как объект, выделенный стекю, который реализует идиому RAII), либо динамический (например, привязанный к времени жизни соединения с базой данных, как в мой предыдущий проект). Когда объект "collector" освобождается, его деструктор освобождает все объекты, на которые он указывает.

Кроме того, как и DrPizza, я считаю, что ограничение на использование шаблонов слишком жесткое. Тем не менее, проведя большую разработку в древних версиях Solaris, AIX и HP-UX (совсем недавно - да, эти платформы все еще живы в Fortune 50), могу вам сказать, что если вы действительно заботитесь о переносимости, вы должен использовать шаблоны как можно меньше. Использование их для контейнеров и интеллектуальных указателей должно быть в порядке, хотя (это сработало для меня). Без шаблонов техника, которую я описал, более болезненна для реализации. Это потребует, чтобы все объекты, управляемые "сборщиком", были получены из общего базового класса.

Ответ 6

G'day,

Я бы посоветовал ознакомиться с соответствующими разделами "Эффективный С++" Скотта Мейерса. Легко читается, и он охватывает некоторые интересные вещи, чтобы заманить неосторожного.

Я также заинтригован отсутствием шаблонов. Так что нет STL или Boost. Ничего себе.

Кстати, чтобы люди согласились на соглашения, это отличная идея. Как и все, чтобы согласиться на соглашения для OOD. BTW В последнем выпуске Effective С++ нет отличной главы о соглашениях OOD, которые были в первом издании, что жалко, например. Соглашения, такие как публичное виртуальное наследование, всегда моделируют отношения "isa".

Rob

Ответ 7

  • Когда вам нужно использовать память управления вручную, убедитесь, что вы вызываете delete В то же самое scope/function/class/module, который когда-либо применяется, например:
  • Позволяет вызывающей функции распределять память, которая заполняется им, не возвращайте новые указатели.
  • Всегда вызывайте delete в той же exe/dll, что и вы вызвали новый, поскольку в противном случае могут возникнуть проблемы с повреждением кучи (разные несовместимые библиотеки времени выполнения).

Ответ 8

вы можете извлечь все из базового класса, реализующего интеллектуальные указатели, такие как функциональность (используя методы ref()/unref() и счетчик.

Все точки, выделенные @Timbo, важны при разработке этого базового класса.