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

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

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

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

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


person Rolle    schedule 10.06.2009    source источник


Ответы (8)


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

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

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

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

person Edouard A.    schedule 10.06.2009
comment
Вы можете безопасно вернуть shared_ptr (или weak_ptr и, несомненно, несколько других реализаций), но не unique_ptr. (Что ж, можешь вернуть unique_ptr&&.) - person Mike C; 06.03.2014
comment
@MikeC: Совершенно безопасно вернуть unique_ptr. А на самом деле почти всегда возвращать unique_ptr&& небезопасно. - person Benjamin Lindley; 08.11.2015

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

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

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

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

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

person Todd Gardner    schedule 10.06.2009

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

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.

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

person iain    schedule 10.06.2009
comment
Я предпочитаю boost :: unique_ptr вместо std :: auto_ptr, потому что сложнее случайно переместить владельца. - person Roman Starkov; 02.01.2010
comment
Спасибо, я не заметил unique_ptr, так как его нет в книге повышения. Это выглядит хорошо. - person iain; 03.01.2010

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

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

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

person Johann Gerell    schedule 10.06.2009
comment
А? Зачем голосовать против этого без объяснения причин? Кто-то, у кого за плечами слишком мало кода крупномасштабных корпоративных приложений? - person Johann Gerell; 12.06.2009
comment
Почему голосование против? Во-первых, я думаю, что ваше использование unique не обязательно означало unique_ptr, но такой зверь существует, и ваш ответ сбивает с толку. Если ты имел ввиду unique_ptr, то как вернуть? (Как я отмечал в другом комментарии выше, unique_ptr&& будет работать.) Во-вторых, ответственность за очистку лежит на самом интеллектуальном указателе, и, возвращая интеллектуальный указатель, вы освобождаете клиента от необходимости беспокоиться об очистке. - person Mike C; 06.03.2014
comment
@MikeC: Я думаю, что ваше использование unique не обязательно означало unique_ptr Это не так, отсюда и использование unique. Я имел в виду все, что ведет себя как std::unique_ptr. но такой зверь существует, и ваш ответ сбивает с толку. Это не зверь, и если это сбивает с толку, я постараюсь переформулировать себя. - person Johann Gerell; 30.05.2014
comment
@MikeC: Если вы имели в виду unique_ptr, то как его вернуть? Как и с любым другим умным указателем: ideone.com/qJnzva (Как я отмечал в другом комментарии выше, unique_ptr && будет работать.) Как показано в ideone.com/qJnzva это не нужно. - person Johann Gerell; 30.05.2014
comment
@MikeC: Во-вторых, ответственность за очистку лежит на умном указателе. Хотя это правда, очистка привязана к области действия вызывающего, что я имел в виду, чтобы было ясно, кто несет ответственность за очистку вверх. - вызывающая сторона управляет очисткой в ​​силу времени жизни переменной, получающей возвращаемый интеллектуальный указатель. возвращая умный указатель, вы освобождаете клиента от необходимости беспокоиться об очистке. Опять же, очистка по-прежнему на 100% контролируется вызывающей стороной, поскольку вызывающая сторона контролирует время жизни / объем присвоенная переменная. - person Johann Gerell; 30.05.2014
comment
@MikeC: К моему ответу добавлен текст, чтобы было понятнее, что я имел в виду. - person Johann Gerell; 30.05.2014

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

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

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

person TimW    schedule 10.06.2009

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

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

person Oliver N.    schedule 10.06.2009
comment
Оператор goto может использоваться (в режиме без спагетти), чтобы гарантировать, что каждая точка выхода проходит через блок кода ... эрго: вы можете свернуть свой собственный throw-try-catch-finally ... мы сделали ... он работает блестяще и без огромных накладных расходов, связанных с обработкой исключений Java / C #. - person corlettk; 10.06.2009

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

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

person Benoît    schedule 10.06.2009
comment
Я не понимаю, что вы имеете в виду. Если они используют auto_ptr или boost :: scoped_ptr, они не могут использовать (или возвращать) что-либо, кроме необработанных указателей. Вы имеете в виду, что функция не может возвращать auto_ptr, или что совместное использование не может быть выражено с помощью auto_ptr? - person Éric Malenfant; 10.06.2009
comment
Я просто имею в виду, что auto_ptr (или scoped_ptr) нельзя использовать в контейнере. - person Benoît; 11.06.2009

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

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

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

  • Y & оператор * () {return * m_internal; }
  • const Y & оператор * () const {return * m_internal; }

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

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

person chila    schedule 11.06.2009
comment
Нет, никогда не возвращать ссылку на shared_ptr. Копировать, или weak_ptr. Копирование shared_ptr (разумно) дешево. - person Mike C; 06.03.2014
comment
Не ссылка, а постоянная ссылка. Мой ответ правильный. Это безопасно и работает лучше всего. - person chila; 28.03.2014