Поверніть розумні вказівники за значенням.
Як ви вже сказали, якщо ви повернете його за посиланням, ви не будете належним чином збільшувати кількість посилань, що відкриває ризик видалити щось у неналежний час. Одного лише цього має бути достатньою причиною, щоб не повертатись за посиланням. Інтерфейси повинні бути надійними.
Проблема витрат в наш час спірна завдяки оптимізації поверненого значення (RVO), тому у вас не виникне послідовність збільшення-збільшення-зменшення або щось подібне у сучасних компіляторах. Тож найкращий спосіб повернути 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()
повертається, ми повинні копіюйте vector
з string
с. Потім нам потрібно скопіювати його ще раз, коли ми ініціалізуємо
names
, і нам потрібно знищити першу копію. Якщо string
у векторі є N s, кожна копія може вимагати виділення пам’яті N + 1 та цілий ряд недружнього доступу до кешу даних>, оскільки копіюється вміст рядка.
Замість того, щоб протистояти такому занепокоєнню, я часто відмовляюся від проходження, щоб уникнути непотрібних копій:
get_names(std::vector<std::string>& out_param );
...
std::vector<std::string> names;
get_names( names );
На жаль, такий підхід далеко не ідеальний.
- Код виріс на 150%
- Нам довелося відмовитись,
const
тому що ми мутуємо імена.
- Як нам люблять нагадувати функціональні програмісти, мутація робить код більш складним для роздумів, підриваючи референційну прозорість та еквівалентні міркування.
- У нас більше немає суворої семантики значень для імен.
Але чи насправді потрібно зіпсувати наш код таким чином, щоб отримати ефективність? На щастя, відповідь виявляється ні (і особливо ні, якщо ви використовуєте C ++ 0x).