Подтвердить что ты не робот

Наилучшая практика при возврате интеллектуальных указателей

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

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

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

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

4b9b3361

Ответ 1

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

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

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

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

Ответ 2

Это зависит от того, каков смысл указателя.

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

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

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

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

Ответ 3

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

boost::shared_ptr

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

boost::weak_ptr / raw pointer

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

std::auto_ptr

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

boost::scoped_ptr

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

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

Ответ 4

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

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

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

Ответ 5

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

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

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

Ответ 6

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

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

Ответ 7

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

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

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

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

Ответ 8

Я бы не поставил исходные указатели в векторы.

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

Ответ 9

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

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

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

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

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

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