Возвращать интеллектуальные указатели по значению.
Как вы сказали, если вы вернете его по ссылке, вы не увеличите счетчик ссылок должным образом, что увеличивает риск удаления чего-либо в неподходящее время. Одного этого должно быть достаточно, чтобы не возвращаться по ссылке. Интерфейсы должны быть надежными.
Озабоченность стоимости в настоящее время спорные благодаря оптимизации возвращаемого значения (РВО), так что вы не будете нести приращение инкремента-декремент последовательности или что - то подобное , что в современных компиляторах. Итак, лучший способ вернуть a shared_ptr
- просто вернуться по значению:
shared_ptr<T> Foo()
{
return shared_ptr<T>(/* acquire something */);
};
Это совершенно очевидная возможность RVO для современных компиляторов C ++. Я точно знаю, что компиляторы Visual C ++ реализуют RVO, даже когда все оптимизации отключены. А с семантикой перемещения C ++ 11 эта проблема еще менее актуальна. (Но единственный способ быть уверенным - это профилировать и экспериментировать.)
Если вы все еще не уверены, у Дэйва Абрахамса есть статья, в которой приводятся аргументы в пользу возврата по значению. Я воспроизвожу здесь отрывок; Я настоятельно рекомендую вам прочитать всю статью:
Скажите честно: какие чувства вызывает у вас следующий код?
std::vector<std::string> get_names();
...
std::vector<std::string> const names = get_names();
Честно говоря, хотя мне следовало бы знать лучше, это меня заставляет нервничать. В принципе, при get_names()
возврате мы должны скопировать a vector
of string
s. Затем нам нужно скопировать его снова при инициализации
names
, и нам нужно уничтожить первую копию. Если string
в векторе есть N s, каждая копия может потребовать до N + 1 выделения памяти и целого ряда недружественных к кешу обращений к данным> по мере копирования содержимого строки.
Вместо того, чтобы противостоять этому виду беспокойства, я часто прибегал к передаче по ссылке, чтобы избежать ненужных копий:
get_names(std::vector<std::string>& out_param );
...
std::vector<std::string> names;
get_names( names );
К сожалению, этот подход далек от идеала.
- Код вырос на 150%
- Нам пришлось отбросить
const
-ness, потому что мы мутируем имена.
- Как любят напоминать функциональные программисты, мутации делают код более сложным для размышлений, подрывая ссылочную прозрачность и уравнительные рассуждения.
- У нас больше нет строгой семантики значений для имен.
Но действительно ли необходимо испортить наш код таким образом, чтобы повысить эффективность? К счастью, ответ оказывается отрицательным (особенно если вы используете C ++ 0x).
cout << "Hello World!";
оператор в конструкторе по умолчанию и в конструкторе копирования, вы не увидите двухHello World!
s, когда действует RVO. Однако это не должно быть проблемой для правильно спроектированных интеллектуальных указателей даже при синхронизации.Что касается любого умного указателя (а не только shared_ptr), я не думаю, что когда-либо приемлемо возвращать ссылку на один, и я бы очень не решался передавать их по ссылке или необработанному указателю. Зачем? Потому что вы не можете быть уверены, что позже он не будет скопирован неглубоко через ссылку. Ваш первый пункт определяет причину, по которой это должно вызывать беспокойство. Это может произойти даже в однопоточной среде. Вам не нужен одновременный доступ к данным, чтобы использовать семантику плохой копии в ваших программах. Вы действительно не контролируете, что ваши пользователи делают с указателем после того, как вы его передаете, поэтому не поощряйте злоупотребление, давая вашим пользователям API достаточно веревки, чтобы повеситься.
Во-вторых, если возможно, посмотрите на реализацию вашего умного указателя. Строительство и разрушение должно быть почти незначительным. Если эти накладные расходы неприемлемы, не используйте умный указатель! Но помимо этого вам также необходимо будет изучить имеющуюся у вас архитектуру параллелизма, поскольку взаимоисключающий доступ к механизму, отслеживающему использование указателя, замедлит вас больше, чем простое построение объекта shared_ptr.
Изменить, 3 года спустя: с появлением более современных функций в C ++ я бы скорректировал свой ответ, чтобы он более приемлем для случаев, когда вы просто написали лямбда, которая никогда не живет за пределами области действия вызывающей функции и не скопировано где-то еще. Здесь, если вы хотите сэкономить очень минимальные накладные расходы на копирование общего указателя, это было бы справедливо и безопасно. Зачем? Потому что вы можете гарантировать, что ссылка никогда не будет использована неправильно.
источник