C ++ - передача посилань на std :: shared_ptr або boost :: shared_ptr


115

Якщо у мене є функція, з якою потрібно працювати з a shared_ptr, чи не було б більш ефективно передавати посилання на нього (щоб уникнути копіювання shared_ptrоб'єкта)? Які можливі погані побічні ефекти? Я передбачаю два можливі випадки:

1) всередині функції робиться копія аргументу, як у

ClassA::take_copy_of_sp(boost::shared_ptr<foo> &sp)  
{  
     ...  
     m_sp_member=sp; //This will copy the object, incrementing refcount  
     ...  
}  

2) всередині функції аргумент використовується тільки, як у

Class::only_work_with_sp(boost::shared_ptr<foo> &sp) //Again, no copy here  
{    
    ...  
    sp->do_something();  
    ...  
}  

Я не бачу в обох випадках вагомих причин передати boost::shared_ptr<foo>значення за значенням замість посилання. Передача за значенням лише "тимчасово" збільшить кількість посилань через копіювання, а потім декрементує її при виході з області дії функції. Я щось переглядаю?

Просто для уточнення, прочитавши декілька відповідей: Я прекрасно погоджуюся з питаннями передчасної оптимізації, і я завжди намагаюся спершу переглядати профіль, а потім працювати на гарячі точки. Моє запитання було скоріше з чисто технічної точки зору коду, якщо ви знаєте, що я маю на увазі.


Я не знаю, чи можете ви змінити теги свого питання, але, будь ласка, спробуйте додати тег підвищення. Я намагався шукати це питання, але не зміг знайти жодного, тому що шукав підсилення та теги смарт-покажчика. Тож я знайшов ваше запитання відразу після того, як склав моє власне запитання
Едісон Густаво Мьенц

Відповіді:


113

Суть різного shared_ptrекземпляра полягає в тому, щоб гарантувати (наскільки це можливо), що поки це shared_ptrбуде в обсягах, об'єкт, на який він вказує, все ще буде існувати, оскільки його посилання буде не менше 1.

Class::only_work_with_sp(boost::shared_ptr<foo> sp)
{
    // sp points to an object that cannot be destroyed during this function
}

Отже, використовуючи посилання на a shared_ptr, ви відключаєте цю гарантію. Отже, у вашому другому випадку:

Class::only_work_with_sp(boost::shared_ptr<foo> &sp) //Again, no copy here  
{    
    ...  
    sp->do_something();  
    ...  
}

Звідки ви знаєте, що sp->do_something()не вибухне через нульовий покажчик?

Все залежить від того, що є в тих розділах коду "...". Що робити, якщо ви зателефонуєте щось під час першого "...", що має побічний ефект (десь в іншій частині коду) очищення a shared_ptrдо цього самого об'єкта? А що, якщо станеться єдиним, що залишився shared_ptrна цьому об'єкті? До побачення об’єкт, саме там, де ви збираєтесь його спробувати і використати.

Отже, є два варіанти відповіді на це питання:

  1. Вивчіть джерело всієї вашої програми дуже уважно, поки ви не переконаєтесь, що об'єкт не загине під час роботи функції.

  2. Змініть параметр назад, щоб він був окремим об'єктом замість посилання.

Загальна порада, яка застосовується тут: не переймайтеся внесенням ризикових змін у свій код заради продуктивності, поки ви не приурочили ваш продукт до реалістичної ситуації в профіле і остаточно не виміряли, що зміна, яку ви хочете внести, внесе значна різниця в продуктивності.

Оновлення для коментатора JQ

Ось надуманий приклад. Це навмисно просто, тому помилка буде очевидною. У реальних прикладах помилка не настільки очевидна, оскільки вона прихована в шарах реальної деталі.

У нас є функція, яка відправить повідомлення кудись. Це може бути велике повідомлення, тому замість того, std::stringщоб скопіювати те, що ймовірно, буде скопійовано під час передачі в декілька місць, ми використовуємо shared_ptrрядок a:

void send_message(std::shared_ptr<std::string> msg)
{
    std::cout << (*msg.get()) << std::endl;
}

(Ми просто "відправляємо" його на консоль для цього прикладу).

Тепер ми хочемо додати засоби для запам'ятовування попереднього повідомлення. Ми хочемо такої поведінки: повинна існувати змінна, яка містить останнє надіслане повідомлення, але, поки повідомлення наразі надсилається, попереднього повідомлення не повинно бути (змінну слід скинути перед відправкою). Тож ми оголошуємо нову змінну:

std::shared_ptr<std::string> previous_message;

Потім ми вносимо зміни до своєї функції відповідно до визначених нами правил:

void send_message(std::shared_ptr<std::string> msg)
{
    previous_message = 0;
    std::cout << *msg << std::endl;
    previous_message = msg;
}

Отже, перш ніж розпочати надсилання, ми відкидаємо поточне попереднє повідомлення, а потім після завершення надсилання ми можемо зберігати нове попереднє повідомлення. Все добре. Ось кілька тестових кодів:

send_message(std::shared_ptr<std::string>(new std::string("Hi")));
send_message(previous_message);

І, як очікувалося, цей друкується Hi!двічі.

Тепер приходить містер Maintainer, який дивиться на код і думає: Ей, цей параметр send_message- це shared_ptr:

void send_message(std::shared_ptr<std::string> msg)

Очевидно, що це можна змінити на:

void send_message(const std::shared_ptr<std::string> &msg)

Подумайте про підвищення продуктивності, яке це принесе! (Не майте на увазі, що ми збираємось надсилати зазвичай велике повідомлення по якомусь каналу, тому підвищення продуктивності буде настільки малим, що не можна виміряти).

Але справжня проблема полягає в тому, що тепер тестовий код буде демонструвати невизначене поведінку (у Visual C ++ 2010 налагодження будується, він руйнується).

Г-н Maintainer здивований цим, але додає захисну перевірку send_message, намагаючись зупинити проблему:

void send_message(const std::shared_ptr<std::string> &msg)
{
    if (msg == 0)
        return;

Але, звичайно, він все одно йде вперед і виходить з ладу, тому що msgніколи не буває нульовим, коли send_messageвикликається.

Як я кажу, з усім кодом, який знаходиться так близько в тривіальному прикладі, легко знайти помилку. Але в реальних програмах, що мають складніші зв’язки між об'єктами, що змінюються, що утримують покажчики один на одного, легко зробити помилку і важко побудувати необхідні тестові випадки для виявлення помилки.

Просте рішення, коли ви хочете, щоб функція могла покладатися на те, що все ще shared_ptrбуде ненульовим протягом усього, полягає в тому, щоб функція виділяла власну істину shared_ptr, а не посилалася на посилання на існуюче shared_ptr.

Мінус полягає в тому, що скопійований a shared_ptrне є безкоштовним: навіть "без блокування" реалізація повинна використовувати заблоковану операцію, щоб виконувати гарантії нитки. Тож можуть виникнути ситуації, коли програму можна значно прискорити, змінивши shared_ptrна «а» shared_ptr &. Але це не та зміна, яку можна сміливо внести до всіх програм. Це змінює логічне значення програми.

Зауважте, що аналогічна помилка може виникнути, якщо ми використовувались у std::stringвсьому std::shared_ptr<std::string>, а не:

previous_message = 0;

щоб очистити повідомлення, ми сказали:

previous_message.clear();

Тоді симптомом буде випадкове надсилання порожнього повідомлення замість невизначеної поведінки. Вартість додаткової копії дуже великого рядка може бути набагато значно більшою, ніж вартість копіювання файлу shared_ptr, тому компроміс може бути різним.


16
Розподілений_птр, що передається, вже живе в області застосування, на сайті виклику. Можливо, вам вдасться створити складний сценарій, коли код у цьому питанні підірветься через звисаючий покажчик, але тоді я припускаю, що у вас є більші проблеми, ніж контрольний параметр!
Магнус Хофф

10
Він може зберігатися в члені. Ви можете зателефонувати щось, що трапиться, щоб очистити цього члена. Вся суть smart_ptr полягає в тому, щоб уникнути необхідності координувати життя в ієрархіях або областях, які гніздяться навколо стека викликів, тому саме краще припустити, що життя не робить цього в таких програмах.
Даніель Ервікер

7
Це насправді не моя точка зору! Якщо ви думаєте, що я кажу, що стосується мого коду, щось конкретне, ви, можливо, не зрозуміли мене. Я говорю про неминуче значення того, що причина shared_ptr існує в першу чергу: багато життєвих об єктів не просто пов'язані з викликами функцій.
Даніель Ервікер

8
@DanielEarwicker повністю згоден з усіма вашими пунктами і дивуюся рівню протилежності. Те, що робить ваші занепокоєння ще більш актуальними, - це нарізання різьби, коли це втягується, гарантії щодо дійсності об'єктів стають набагато важливішими. Хороша відповідь.
радман

3
Не так давно я переслідував дуже серйозну помилку, яка була пов'язана з передачею посилання на загальний покажчик. Код обробляв зміну стану об'єкта, і коли він помітив, що стан об'єкта змінився, він видалив його із колекції об'єктів у попередньому стані та перемістив його до колекції об’єктів у новому стані. Операція видалення знищила останній спільний покажчик на об’єкт. Функція-член була викликана посиланням на загальний покажчик у колекції. Бум. Даніель Ервікер прав.
Девід Шварц

115

Я не погодився з найвищою оцінкою відповіді, тому пішов шукати експертні думки, і ось вони. Від http://channel9.msdn.com/Shows/Going+Deep/C-and-Beyond-2011-Scott-Andrei-and-Herb-Ask-Us-Anything

Herb Sutter: "коли ви передаєте shared_ptrs, копії коштують дорого"

Скотт Майєрс: "У розділі shared_ptr немає нічого особливого, якщо мова йде про те, чи передаєте ви його за значенням чи передаєте його за посиланням. Використовуйте точно той же аналіз, який ви використовуєте для будь-якого іншого визначеного користувачем типу. Люди, схоже, мають таке сприйняття, що shared_ptr якось вирішує. всі проблеми з управлінням, і тому, що він невеликий, його обов'язково недорого передати за вартістю. Це потрібно скопіювати, і з цим пов'язана вартість ... дорого передати його за вартістю, тому якщо я можу піти це з належною семантикою в моїй програмі, я передаю її посиланням на const або посилання замість цього "

Герб Саттер: "завжди передайте їх посиланням на const, а дуже часто, можливо, тому що ви знаєте, що ви викликали, може змінити те, на що ви отримали посилання, можливо, тоді ви можете перейти за значенням ... якщо ви скопіюєте їх як параметри, о Боже мій, ти майже ніколи не потребуєш обробляти цю кількість посилань, тому що вона все одно залишається в живих, і ти маєш передати це посиланням, тож, будь ласка, зроби це "

Оновлення: Трава поширилася на це тут: http://herbsutter.com/2013/06/05/gotw-91-solution-smart-pointer-parameters/ , хоча мораль історії полягає в тому, що ви не повинні проходити shared_ptrs взагалі ", якщо ви не хочете використовувати або керувати самим розумним вказівником, наприклад, для того, щоб поділитись або передати право власності".


8
Приємна знахідка! Приємно бачити, як двоє головних експертів з цього питання публічно спростовують загальноприйняту мудрість щодо ТА
Стефан Толксдорф

3
"Немає нічого особливого в shared_ptr, якщо мова йде про те, передаєте ви його за значенням чи передаєте його за посиланням" - я дійсно з цим не згоден. Це особливе. Особисто я вважаю за краще зіграти в безпеку і взяти невеликий хіт виступу. Якщо є певна область коду, яку мені потрібно оптимізувати, то переконайтесь, я б розглядав переваги продуктивності передачі shared_ptr від const ref.
JasonZ

3
Цікаво також зазначити, що, хоча йшлося про домовленість про перевитрату shared_ptrs, не було домовленостей щодо питання прохідної вартості та порівняння.
Ніколь Болас

2
Скотт Майєрс: "Так що, якщо я можу відмовитися від цього відповідною семантикою в моїй програмі ...", тобто зовсім не суперечить моїй відповіді, що вказує на те, що розібратися, чи змінити параметри на const &вплив на семантику буде легко лише дуже прості програми.
Даніель Ервікер

7
Герб Саттер: "дуже часто, можливо, тому що ви знаєте, що ви назвали, можливо, змінити те, на що ви отримали посилання". Знову ж таки, це невелике звільнення від другорядного випадку, тож воно не суперечить моїй відповіді. Залишається питання: як ти знаєш, що безпечно використовувати const ref? Дуже легко довести в простій програмі, не так просто в складній програмі. Але агов, це є C ++, і тому ми вважаємо за краще передчасного мікро-оптимізації в протягом майже все інші інженерні проблеми, правильно?! :)
Daniel Earwicker

22

Я б радив проти цієї практики, якщо ви та інші програмісти, з якими ви працюєте, справді не знаєте, що все робите.

По-перше, ви не знаєте, як може розвиватися інтерфейс для вашого класу, і ви хочете не допустити інших програмістів робити погані справи. Передача shared_ptr за посиланням - це не те, що програміст повинен очікувати, тому що це не ідіоматично, а це дозволяє легко використовувати його неправильно. Програма захисно: зробіть інтерфейс важким для неправильного використання. Проходження посилань буде просто запрошувати проблеми пізніше.

По-друге, не оптимізуйте, поки ви не дізнаєтесь, що саме цей клас стане проблемою. Спочатку профіль, а потім, якщо вашій програмі дійсно потрібен стимул, наданий шляхом проходження посилання, то, можливо. В іншому випадку не закладайте дрібні речі (тобто додаткові N інструкцій, необхідних для передачі за значенням), а не турбуйтеся про дизайн, структуру даних, алгоритми та довгострокову ремонтоздатність.


Хоча відповідь латб є технічно правильною, ніколи не варто недооцінювати "лінь" програмістів (я теж лінивий!). Відповідь Littlenag краще, що посилання на shared_ptr буде несподіваним і, можливо, непотрібною оптимізацією, що робить подальше обслуговування більш складним.
netjeff

18

Так, взяти довідку там добре. Ви не збираєтесь надавати методу спільного володіння; вона лише хоче з цим працювати. Ви також можете взяти посилання на перший випадок, оскільки копіюєте його. Але для першого випадку це займає право власності. Цей трюк все-таки скопіювати лише один раз:

void ClassA::take_copy_of_sp(boost::shared_ptr<foo> sp) {
    m_sp_member.swap(sp);
}

Ви також повинні копіювати, коли ви повертаєте його (тобто не повертаєте посилання). Тому що ваш клас не знає, що з ним робить клієнт (він може зберігати вказівник на нього і тоді трапляється великий удар). Якщо згодом виявиться, що це вузьке місце (перший профіль!), Тоді ви все одно можете повернути посилання.


Редагувати : Звичайно, як зазначають інші, це справедливо лише в тому випадку, якщо ви знаєте свій код і знаєте, що певним чином не скидаєте переданий загальний покажчик. Якщо ви сумніваєтесь, просто перейдіть за значенням


11

Доцільно пройти повз shared_ptrs const&. Це, швидше за все, не спричинить проблем (за винятком випадків, коли посилання shared_ptrбуде видалено під час виклику функції, докладно описаного Earwicker), і це, швидше за все, буде швидше, якщо ви передасте багато з них навколо. Пам'ятайте; за замовчуванням boost::shared_ptr- безпечна для потоку, тому його копіювання включає збільшення потоку.

Спробуйте скористатися, const&а не просто &, тому що тимчасові об'єкти можуть не передаватися без посилання. (Навіть незважаючи на те, що розширення мови в MSVC дозволяє це робити в будь-якому випадку)


3
Так, я завжди використовую посилання на const, я просто забув поставити це у свій приклад. У будь-якому випадку, MSVC дозволяє прив'язувати посилання, що не мають const, до тимчасових журналів не для помилки, а тому, що за замовчуванням у нього властивість "C / C ++ -> Мова -> Вимкнути розширення мови" встановлено на "НІ". Увімкніть це, і вони не
збиратимуть

abigagli: Серйозно? Солодке! Я накладу це на роботі, перше, що завтра;)
Магнус Хофф

10

У другому випадку зробити це простіше:

Class::only_work_with_sp(foo &sp)
{    
    ...  
    sp.do_something();  
    ...  
}

Ви можете назвати це як

only_work_with_sp(*sp);

3
Якщо ви приймаєте умову використовувати посилання на об'єкти, коли вам не потрібно брати копію вказівника, він також може документувати ваш намір. Це також дає можливість скористатися посиланням на const.
Марк Викуп

Так, я погоджуюся на використання посилань на об'єкти як на засоби вираження того, що викликана функція нічого не "запам'ятовує" про цей об'єкт. Зазвичай я використовую формальні аргументи вказівника, якщо функція "відслідковує" об'єкт
abigagli

3

Я б уникнув "простого" посилання, якщо тільки функція явно не може змінити покажчик.

A const &може бути розумною мікрооптимізацією під час виклику невеликих функцій - наприклад, для подальшої оптимізації, як, наприклад, виділення деяких умов. Також приріст / декремент - оскільки це безпечно для потоків - є точкою синхронізації. Я б не очікував, що це призведе до великої зміни в більшості сценаріїв.

Як правило, ви повинні використовувати простіший стиль, якщо у вас немає причин цього не робити. Потім або використовуйте const &послідовно, або додайте коментар, чому, якщо ви використовуєте його лише в кількох місцях.


3

Я б закликав передавати спільний покажчик через посилання const - семантику, що функція, що передається за допомогою вказівника, НЕ володіє вказівником, що є чистою ідіомою для розробників.

Єдина підводка полягає в декількох програмах ниток, об'єкт, на який вказує спільний покажчик, знищується в іншій потоці. Отже, можна сказати, що використання посилання const на загальний вказівник безпечно в програмі з однопотоковою передачею.

Передача спільного вказівника за допомогою безвідмовних посилань іноді небезпечна - причиною є функції заміни та скидання, функція може викликати всередині, щоб знищити об'єкт, який досі вважається дійсним після повернення функції.

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

Всього мої 2 копійки :-)


1
Дивіться коментар Девіда Шварца вище "... Я переслідував дуже серйозну помилку, яка була пов'язана з передачею посилання на загальний покажчик. Код обробляв зміну стану об'єкта, і коли він помітив, що стан об'єкта змінився, він видалив його із колекції об'єктів у попередньому стані та перемістив його до колекції об’єктів у новому стані. Операція видалення знищила останній спільний покажчик на об’єкт. Функція члена викликалася посиланням на загальний покажчик у колекції. Бум ... "
Джейсон Харрісон

3

Здається, що всі плюси і мінуси тут насправді можна узагальнити для будь-якого типу, переданого посиланням, а не просто shared_ptr. На мою думку, ви повинні знати семантику проходження за посиланням, const посилання та значення і правильно використовувати його. Але абсолютно нічого поганого в передачі shared_ptr за посиланням немає, якщо ви не вважаєте, що всі посилання погані ...

Повернутися до прикладу:

Class::only_work_with_sp( foo &sp ) //Again, no copy here  
{    
    ...  
    sp.do_something();  
    ...  
}

Звідки ти знаєш, що sp.do_something()не вибухне через звисаючу вказівку?

Правда полягає в тому, що, shared_ptr чи ні, const чи ні, це може статися, якщо у вас є недолік дизайну, як-от прямо чи опосередковано ділитися правом власності на spміж потоками, якщо ви неправильно використовуєте об'єкт, який delete thisви робите , у вас є кругле право власності або інші помилки власності.


2

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

Наприклад, цей код призведе до помилки, але він буде працювати, якщо змінити, test()щоб загальний покажчик не передався посиланням.

#include <boost/shared_ptr.hpp>

class Base { };
class Derived: public Base { };

// ONLY instances of Base can be passed by reference.  If you have a shared_ptr
// to a derived type, you have to cast it manually.  If you remove the reference
// and pass the shared_ptr by value, then the cast is implicit so you don't have
// to worry about it.
void test(boost::shared_ptr<Base>& b)
{
    return;
}

int main(void)
{
    boost::shared_ptr<Derived> d(new Derived);
    test(d);

    // If you want the above call to work with references, you will have to manually cast
    // pointers like this, EVERY time you call the function.  Since you are creating a new
    // shared pointer, you lose the benefit of passing by reference.
    boost::shared_ptr<Base> b = boost::dynamic_pointer_cast<Base>(d);
    test(b);

    return 0;
}

1

Я припускаю, що вам знайома передчасна оптимізація і просите це або в академічних цілях, або тому, що ви виділили якийсь попередній код, який недостатньо працює.

Пройти посилання за порядком

Передача посилання const краще і зазвичай може бути використана, оскільки це не примушує стабільність на вказаний об'єкт.

Ви не ризикуєте втратити покажчик через використання посилання. Це посилання є свідченням того, що у вас є копія смарт-покажчика раніше в стеці, і лише один потік володіє стеком виклику, так що попередня копія не пропадає.

Використання посилань часто є більш ефективним із зазначених вами причин, але не гарантується . Пам'ятайте, що перенаправлення об'єкта теж може зайняти роботу. Вашим ідеальним сценарієм використання опорного використання було б, якщо ваш стиль кодування передбачає безліч дрібних функцій, де вказівник переходить з функції в функцію до функції перед використанням.

Ви завжди повинні уникати зберігання вашого розумного вказівника як еталону. Ваш Class::take_copy_of_sp(&sp)приклад показує правильне використання для цього.


1
"Ви не ризикуєте втратити покажчик через використання посилання. Це посилання є свідченням того, що у вас є копія смарт-вказівника раніше в стеці" Або член даних ...?
Даніель Ервікер

Розглянемо магію boost :: thread і boost :: ref: boost :: function <int> functionPointer = boost :: bind (doSomething, boost: ref (sharedPtrInstance)); m_workerThread = new boost :: thread (functionPointer); ... видалити sharedPtrInstance
Jason Harrison

1

Якщо припустити, що ми не переймаємося правильністю const (або більше, ти маєш на увазі дозволити функціям можливість змінювати або ділити право власності на передані дані), передача boost :: shared_ptr за значенням безпечніше, ніж передавати його за посиланням як ми дозволяємо оригінальному boost :: shared_ptr контролювати власний термін експлуатації. Розглянемо результати наступного коду ...

void FooTakesReference( boost::shared_ptr< int > & ptr )
{
    ptr.reset(); // We reset, and so does sharedA, memory is deleted.
}

void FooTakesValue( boost::shared_ptr< int > ptr )
{
    ptr.reset(); // Our temporary is reset, however sharedB hasn't.
}

void main()
{
    boost::shared_ptr< int > sharedA( new int( 13 ) );
    boost::shared_ptr< int > sharedB( new int( 14 ) );

    FooTakesReference( sharedA );

    FooTakesValue( sharedB );
}

З наведеного вище прикладу ми бачимо, що передача sharedA за посиланням дозволяє FooTakesReference скинути вихідний покажчик, що зменшує кількість використання до 0, знищуючи його дані. FooTakesValue , однак, не може скинути початковий покажчик, гарантуючи , що дані sharedB все ще можуть бути використані. Коли інший розробник неминуче приходить до себе і намагається виправити скарб на тендітне існування sharedA , настає хаос. Однак щасливий розробник sharedB повертається додому рано, як у його світі все добре.

Безпека коду в цьому випадку значно переважає будь-яке створення копіювання, яке покращує швидкість. У той же час, boost :: shared_ptr призначений для підвищення безпеки коду. Набагато простіше буде перейти від копії до посилання, якщо щось вимагає такої оптимізації ніші.


1

Сенді писав: "Здається, всі плюси і мінуси тут насправді можна узагальнити для будь-якого типу, переданого посиланням, а не просто shared_ptr."

В деякій мірі це правда, але сенс використання shared_ptr полягає в тому, щоб усунути занепокоєння щодо життя об'єкта і дозволити компілятору обробляти це за вас. Якщо ви збираєтеся передати загальний покажчик за посиланням і дозволити клієнтам ваших методів обліку посилань-обчислених об'єктів неконструктивних методів, які можуть звільнити дані об'єкта, то використовувати спільний покажчик майже безглуздо.

У цьому попередньому реченні я написав «майже», тому що ефективність може викликати занепокоєння, і це може «бути виправданим у рідкісних випадках, але я також сам би уникав цього сценарію і шукав усі можливі інші рішення щодо оптимізації, такі як серйозно дивитися при додаванні іншого рівня непрямості, лінивої оцінки тощо.

Код, який існує минулого його автора, або навіть публікація його пам'яті автора, що вимагає неявних припущень щодо поведінки, зокрема поведінки щодо життя об'єкта, вимагає чіткої, стислої, читабельної документації, і тоді багато клієнтів не будуть читати її все одно! Простота майже завжди здобуває ефективність, і майже завжди існують інші способи бути ефективними. Якщо вам дійсно потрібно передавати значення за посиланням, щоб уникнути глибокого копіювання конструкторами копій ваших об'єктів, що рахуються посиланням (та оператором рівності), то, можливо, вам слід розглянути способи зробити так, щоб скопійовані дані були перераховані посиланнями, які можуть бути скопіюється швидко. (Звичайно, це лише один проектний сценарій, який може не стосуватися вашої ситуації).


1

Раніше я працював над проектом, що принцип дуже сильний щодо передачі інтелектуальних покажчиків за значенням. Коли мене попросили зробити деякий аналіз продуктивності - я виявив, що для збільшення та зменшення опорних лічильників смарт-покажчиків програма витрачає між 4-6% використовуваного процесорного часу.

Якщо ви хочете передати розумні покажчики цінністю, щоб уникнути виникнення проблем у дивних випадках, як це описано від Daniel Earwicker, переконайтеся, що ви розумієте ціну, яку ви платите за неї.

Якщо ви вирішили перейти з посиланням, головна причина використовувати посилання const - це зробити можливим неявне оновлення, коли вам потрібно передавати спільний покажчик об’єкту від класу, який успадковує клас, який ви використовуєте в інтерфейсі.


0

На додаток до сказаного в litb, я хотів би зазначити, що, ймовірно, це пройде через посилання const у другому прикладі, таким чином ви впевнені, що не випадково його модифікуєте.


0
struct A {
  shared_ptr<Message> msg;
  shared_ptr<Message> * ptr_msg;
}
  1. пройти за значенням:

    void set(shared_ptr<Message> msg) {
      this->msg = msg; /// create a new shared_ptr, reference count will be added;
    } /// out of method, new created shared_ptr will be deleted, of course, reference count also be reduced;
  2. пройти за посиланням:

    void set(shared_ptr<Message>& msg) {
     this->msg = msg; /// reference count will be added, because reference is just an alias.
     }
  3. пройти повз покажчик:

    void set(shared_ptr<Message>* msg) {
      this->ptr_msg = msg; /// reference count will not be added;
    }

0

Кожен фрагмент коду повинен мати певний сенс. Якщо ви передаєте загальний покажчик за значенням скрізь у програмі, це означає, що "я не впевнений у тому, що відбувається в інших місцях, отже, я віддаю перевагу безпеці ". Це не те, що я називаю хорошим знаком довіри іншим програмістам, які могли б ознайомитися з кодом.

У будь-якому випадку, навіть якщо функція отримує посилання на const і ви не впевнені, ви все одно можете створити копію спільного покажчика на чолі функції, щоб додати чітке посилання на покажчик. Це також можна розглядати як натяк на дизайн ("вказівник міг бути модифікований в іншому місці").

Так, так, IMO, за замовчуванням має бути " пройти через посилання const ".

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.