лучшая практика при возврате умных указателей - PullRequest
31 голосов
/ 10 июня 2009

Каков наилучший метод при возвращении умного указателя, например boost :: shared_ptr? Должен ли я по умолчанию вернуть смарт-указатель или базовый необработанный указатель? Я пришел из C #, поэтому я склонен всегда возвращать умные указатели, потому что это кажется правильным. Как это (пропуская const -корректность для более короткого кода):

class X
{
public:
    boost::shared_ptr<Y> getInternal() {return m_internal;}

private:
    boost::shared_ptr<Y> m_internal;
}

Однако я видел, как некоторые опытные кодеры возвращали необработанный указатель и помещали необработанные указатели в векторы. Как правильно это сделать?

Ответы [ 9 ]

23 голосов
/ 10 июня 2009

Нет «правильного» пути. Это действительно зависит от контекста.

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

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

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

11 голосов
/ 10 июня 2009

Зависит от значения указателя.

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

Возвращение необработанного указателя говорит: «Вы знаете об этом объекте, но не владеете им». Это способ передать управление, но не привязывать время жизни к первоначальному владельцу.

(в некоторых старых c-программах это означает, что «теперь ваша проблема - удалить меня», но я настоятельно рекомендую избегать этого)

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

8 голосов
/ 10 июня 2009

Я следую следующим указаниям для передачи аргументов указателей в функции и возврата указателей:

boost::shared_ptr

API и клиент разделяют права собственности на этот объект. Однако вы должны быть осторожны, чтобы избежать циклических ссылок с shared_ptr, если объекты представляют собой какой-то график. По этой причине я пытаюсь ограничить использование shared_ptr.

boost::weak_ptr / raw pointer

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

std::auto_ptr

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

boost::scoped_ptr

Для указателей на объекты, хранящиеся в стеке или в качестве переменных членов класса. Сначала я пытаюсь использовать scoped_ptr.

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

7 голосов
/ 10 июня 2009

Обычно я возвращаю «владеющие» / «уникальные» умные указатели с заводов или аналогичные, чтобы было ясно, кто несет ответственность за очистку.

В этом примере https://ideone.com/qJnzva показано, как вернуть std::unique_ptr, который будет удален, когда область действия переменной, которой назначает вызывающее значение, выходит за пределы области.

Хотя интеллектуальный указатель действительно удаляет свой собственный указатель, время жизни переменной, содержащей интеллектуальный указатель, на 100% контролируется вызывающей стороной, поэтому вызывающая сторона решает, когда указатель удален. Однако, поскольку это «уникальный» и «владеющий» умный указатель, никакой другой клиент не может контролировать время жизни.

4 голосов
/ 10 июня 2009

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

Если вы вернете слабый_птр, маловероятно, что в приложении будут висячие указатели.

В случае проблем с производительностью я бы вернул ссылку на объект и метод hasValidXObject.

3 голосов
/ 10 июня 2009

По моему мнению, в C ++ вы всегда должны оправдывать использование неохраняемого указателя.

Может быть много веских причин: необходимость в очень высокой производительности, очень низком использовании памяти для работы с устаревшими библиотеками из-за некоторой проблемы с базовой структурой данных, которую хранит указатель. Но [динамически распределяемые] указатели являются несколько «злыми» в том смысле, что вам приходится освобождать память при каждом возможном пути выполнения, и вы почти наверняка забудете один.

0 голосов
/ 11 июня 2009

const boost :: shared_ptr & getInternal () {return m_internal;}

Это позволяет избежать копирования.

Иногда вы захотите вернуть ссылку, например:

  • Y & operator * () {return * m_internal; }
  • const Y & operator * () const {return * m_internal; } * +1010 *

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

4 хороши в зависимости от целей. Этот вопрос требует более обстоятельного обсуждения.

0 голосов
/ 10 июня 2009

Я бы не помещал необработанные указатели в векторы.

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

0 голосов
/ 10 июня 2009

зависит от ваших целей.

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

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

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

...