Использование интеллектуальных указателей в качестве стандарта программирования? - PullRequest
14 голосов
/ 26 мая 2011

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

Что такое правильный подход к программированию на использование умных указателей, пожалуйста?Должны ли они действительно использоваться, даже если я проверю утечки памяти на выделенных блоках памяти?Это все еще зависит от меня?Если я их не использую, можно ли это считать слабостью программирования?

Если настоятельно рекомендуются умные указатели (например, std :: auto_ptr), следует ли мне использовать их вместо каждого открытого указателя

Ответы [ 9 ]

22 голосов
/ 26 мая 2011

Вы должны использовать RAII для обработки всех распределений ресурсов.

Умные указатели являются лишь одним частным частным случаем этого правила.

А умные указатели - это больше, чем просто shared_ptr.Существуют разные умные указатели с разной семантикой владения.Используйте тот, который соответствует вашим потребностям.(Основные из них: scoped_ptr, shared_ptr, weak_ptr и auto_ptr / unique_ptr (предпочитайте последний, где он доступен). В зависимости от вашего компилятора они могут быть доступны в стандартной библиотеке как часть TR1или нет вообще, и в этом случае вы можете получить их через библиотеки Boost.

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

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

10 голосов
/ 26 мая 2011

Нельзя просто слепо подставлять std::auto_ptr для каждого необработанного указателя. В частности, auto_ptr передает право собственности по назначению, что отлично подходит для некоторых целей, но определенно не для других.

Существует реальная причина, по которой существует несколько разновидностей интеллектуальных указателей (например, shared_ptr, weak_ptr, auto_ptr / unique_ptr и т. Д.). Каждый из них выполняет свою цель. Одним из основных недостатков «сырого» указателя является то, что он имеет очень много разных применений (и обладает этой универсальностью в значительной степени потому, что он мало или совсем не помогает в какой-либо одной цели). Умные указатели имеют тенденцию быть более специализированными, что означает, что они могут быть более умными в выполнении одной вещи хорошо, но также означает, что вы должны выбрать правильный для своей работы, или это в конечном итоге приведет к неправильным вещам.

4 голосов
/ 26 мая 2011

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

Независимо от подхода стоит упомянуть несколько моментов:

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

  • Что касается объекта сущности, который моделирует что-то в домен приложения: они должны быть созданы и уничтожены в соответствии с к логике программы. Независимо от того, есть ли указатели на их или нет. Если их уничтожение вызывает проблемы, то у вас есть ошибка в логике вашей программы (неправильная обработка события, и т.д.), и использование умных указателей ничего не изменит.

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

Что вы обычно найдете, когда люди пытаются использовать умные указатели везде - это утечки памяти; типичные счетчики ссылок не обрабатывать циклы, и, конечно же, типичные приложения полны циклов: Connection будет указывать на Client, который подключен к нему, и Client будет содержать список Connection, к которому он подключен. И если умный указатель boost::shared_ptr, есть также определенный риск свисающих указателей: создать два легко boost::shared_ptr по тому же адресу (что приводит к двум счетчикам для ссылок).

4 голосов
/ 26 мая 2011

Если настоятельно рекомендуются умные указатели (например, std :: auto_ptr), следует ли мне использовать их вместо каждого обнаженного указателя?

На мой взгляд, да, вы должны использовать его для каждого указателя, которым вы владеете.

Вот мои идеи по управлению ресурсами в C ++ (не стесняйтесь не соглашаться):

  1. Хорошее управление ресурсами требует мышления с точки зрения владения.
  2. Ресурсы должны управляться объектами (RAII).
  3. Обычно единоличное владение предпочтительнее, чем долевое.
  4. В идеале создатель является также владельцем объекта. (Тем не менее, существуют ситуации, когда передача прав в порядке.)


Это приводит к следующим практикам:

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

  2. Используйте boost::shared_ptr для контейнеров или для совместного владения:

    // Container of MyClass* pointers:
    typedef boost::shared_ptr<MyClass> MyClassPtr;
    std::vector<MyClassPtr> vec;

  3. std::auto_ptr (C ++ 03) может использоваться для передачи права собственности. Например, в качестве возвращаемого значения фабричных или клонированных методов:

    // Factory method returns auto_ptr
    std::auto_ptr<Button> button = Button::Create(...);

    // Clone method returns auto_ptr
    std::auto_ptr<MyClass> copy = obj->clone();

    // Use release() to transfer the ownership to a scoped_ptr or shared_ptr
    boost::scoped_ptr<MyClass> copy(obj->clone().release());

  4. Если вам нужно сохранить указатель, который вам не принадлежит, вы можете использовать необработанный указатель:

    this->parent = inParentObject;

  5. В определенных ситуациях требуется boost::weak_pointer. См. документацию для получения дополнительной информации.

4 голосов
/ 26 мая 2011

Умные указатели позволяют автоматически определять время жизни объектов, к которым оно относится. Это главное, что нужно понять.

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

Для каждого объекта вы должны подумать о жизненном цикле, через который он пройдет, а затем выбрать одно из самых простых правильных и эффективных решений. Иногда это будет shared_ptr, потому что вы хотите, чтобы объект использовался несколькими компонентами и был автоматически уничтожен, если он больше не используется. Иногда вам нужен объект только в текущей области видимости / родительском объекте, поэтому scoped_ptr может быть более подходящим. Иногда вам нужен только один владелец экземпляра, так что unique_ptr подходит. Может быть, вы найдете случаи, когда вы знаете алгоритм, который может определить / автоматизировать время жизни объекта, поэтому вы напишите свой собственный умный указатель для него.

Например, в противном случае использование пулов запрещает использовать smart_ptr. Обнаженные указатели могут быть более желанным простым и эффективным решением в данном конкретном (но распространенном в встроенном программном обеспечении) случае.

См. Этот ответ (от меня) для большего количества объяснений: https://softwareengineering.stackexchange.com/questions/57581/in-c-is-it-a-reflection-of-poor-software-design-if-objects-are-deleted-manuall/57611#57611

4 голосов
/ 26 мая 2011

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

YES
Цель интеллектуальных указателей состоит в том, чтобы помочь вам реализовать RAII (SBRM) , что в основном позволяет самому ресурсу взять на себя ответственность за его освобождение и ресурс не нужно явно полагаться на вас , помня , чтобы освободить его.

Если я их не использую, можно ли это считать слабостью программирования?

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

Если интеллектуальные указатели (например: std :: auto_ptr) настоятельно рекомендуются, следует ли использовать их вместо каждого обнаженного указателя?

YES
Вы должны использовать интеллектуальные указатели везде, где это возможно, потому что просто нет недостатка в их использовании, а есть только многочисленные преимущества их использования.
Не используйте auto_ptr, потому что это уже устарело! Существуют различные другие умные указатели, которые вы можете использовать в зависимости от требований. Вы можете обратиться по ссылке выше, чтобы узнать больше о них.

2 голосов
/ 26 мая 2011

Как правило, вы предпочитаете умные указатели, но есть несколько исключений.

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

Умные указатели используются для управления временем жизни объекта. Часто, когда вы передаете указатель на функцию, функция не влияет на время жизни; функция не пытается удалить объект и не сохраняет копию указателя. Вызывающий код не может удалить объект, пока функция не вернется. В этом случае тупой указатель вполне приемлем.

2 голосов
/ 26 мая 2011

Да. Предполагая, что у вас есть C ++ 0x, используйте unique_ptr или shared_ptr (в зависимости от ситуации), чтобы обернуть все необработанные указатели, которые вы new подняли. С помощью make_shared, shared_ptr обладает высокой производительностью. Если вам не нужен подсчет ссылок, то unique_ptr поможет вам лучше. Они оба ведут себя должным образом в коллекциях и других обстоятельствах, где auto_ptr был тупым указателем.

0 голосов
/ 23 июня 2011

Использование умных указателей (shared_ptr или иным образом) ВЕЗДЕ - плохая идея. Полезно использовать shared_ptr для управления временем жизни объектов / ресурсов, но не рекомендуется передавать их в качестве параметров функциям и т. Д. Это увеличивает вероятность циклических ссылок и других чрезвычайно трудных для отслеживания ошибок (Личный опыт: попробуйте выяснить, кто не следует удерживать ресурс в 2 миллионах строк кода, если каждый вызов функции изменяет счетчик ссылок - в конечном итоге вы подумаете, что парни, которые делают подобные вещи, - м *** нс). Лучше передать необработанный указатель или ссылку. Ситуация еще хуже в сочетании с ленивым воплощением. Я бы предложил разработчикам знать жизненный цикл объектов, которые они пишут, и использовать shared_ptr для управления этим (RAII), но не расширять использование shared_ptr за пределы этого.

...