C++ - przekazywanie odniesień do std:: shared ptr lub boost:: shared ptr

Jeśli mam funkcję, która musi pracować z shared_ptr, czy nie byłoby bardziej efektywne przekazanie jej odniesienia do niej (aby uniknąć kopiowania obiektu shared_ptr)? Jakie są możliwe złe skutki uboczne? Wyobrażam sobie dwa możliwe przypadki:

1) wewnątrz funkcji powstaje Kopia argumentu, jak w

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

2) wewnątrz funkcji argument jest używany tylko, jak w

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

Nie widzę w obu przypadkach dobrego powodu, aby przekazać boost::shared_ptr<foo> przez wartość zamiast przez odniesienie. Przejście przez wartość tylko" tymczasowo " zwiększy liczbę referencji z powodu kopiowania, a następnie zmniejszy ją po wyjściu z zakresu funkcji. Czy coś przeoczyłem?

Tak dla wyjaśnienia, po przeczytaniu kilku odpowiedzi: doskonale zgadzam się co do kwestii przedwczesnej optymalizacji i zawsze staram się najpierw-profil-potem-pracować-na-hotspotach. Moje pytanie było bardziej z czysto technicznego punktu widzenia, jeśli wiesz, co mam na myśli.

Author: Null, 2008-11-29

17 answers

Punktem odrębnej instancji shared_ptr jest zagwarantowanie (tak dalece jak to możliwe), że dopóki ta shared_ptr jest w zasięgu, obiekt, do którego wskazuje, nadal będzie istniał, ponieważ jego liczba referencji będzie wynosić co najmniej 1.

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

Więc używając odniesienia do shared_ptr, wyłączasz tę gwarancję. Więc w drugim przypadku:

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

Skąd wiesz, że sp->do_something() nie wybuchnie z powodu wskaźnika null?

Wszystko zależy, co jest w nich"..."sekcje Kodeksu. Co jeśli zadzwoń coś podczas pierwszego"...'który ma efekt uboczny (gdzieś w innej części kodu) oczyszczenia shared_ptr z tego samego obiektu? A co, jeśli będzie jedynym pozostałym odrębnym shared_ptr dla tego obiektu? / Align = "left" /

Są więc dwa sposoby na odpowiedź na to pytanie:

  1. Dokładnie zbadaj źródło całego programu, dopóki nie będziesz pewien, że obiekt nie umrze podczas funkcji ciało.

  2. Zmień parametr z powrotem na odrębny obiekt zamiast odniesienia.

Ogólna rada, która ma zastosowanie tutaj: nie zawracaj sobie głowy wprowadzaniem ryzykownych zmian w kodzie ze względu na wydajność, dopóki nie określisz swojego produktu w realistycznej sytuacji w profilerze i ostatecznie zmierzysz, że zmiana, którą chcesz wprowadzić, spowoduje znaczącą różnicę w wydajności.

Update for commenter JQ

Oto wymyślony przykład. To celowo proste, więc błąd będzie oczywisty. W rzeczywistych przykładach błąd nie jest tak oczywisty, ponieważ jest ukryty w warstwach rzeczywistych szczegółów.

Mamy funkcję, która wyśle gdzieś wiadomość. Może to być duża wiadomość, więc zamiast używać std::string, która prawdopodobnie zostanie skopiowana, gdy zostanie przekazana do wielu miejsc, używamy shared_ptr do ciągu znaków:

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

(po prostu" wysyłamy " go do konsoli w tym przykładzie).

Teraz chcemy dodać możliwość zapamiętania poprzedniej wiadomości. Chcemy zachować następujące zachowanie: zmienna musi istnieć, która zawiera ostatnio wysłaną wiadomość, ale podczas gdy wiadomość jest aktualnie wysyłana, nie może być żadnej poprzedniej wiadomości (zmienna powinna zostać zresetowana przed wysłaniem). Tak więc deklarujemy nową zmienną:

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

Następnie zmieniamy naszą funkcję zgodnie z zasadami, które określiliśmy:

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

Więc zanim zaczniemy wysyłać odrzucamy bieżącą poprzednią wiadomość, a następnie po wyślij jest zakończone możemy zapisać nową poprzednią wiadomość. Wszystko dobrze. Oto kod testowy:

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

I zgodnie z oczekiwaniami, to drukuje Hi! dwa razy.

Nadchodzi Pan opiekun, który patrzy na kod i myśli: Hej, ten parametr do send_message to shared_ptr:

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

Oczywiście można to zmienić na:

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

Pomyśl o ulepszeniu wydajności, jakie to przyniesie! (Nie ważne, że mamy zamiar wysłać typowo dużą wiadomość przez jakiś kanał, więc zwiększenie wydajności będzie tak małe, że będzie niemożliwe do zmierzenia).

Ale prawdziwym problemem jest to, że teraz kod testowy będzie wykazywał nieokreślone zachowanie(w kompilacjach debugowania Visual C++ 2010 ulega awarii).

Jest zaskoczony, ale dodaje kontrolę obronną do send_message, aby zapobiec wystąpieniu problemu:
void send_message(const std::shared_ptr<std::string> &msg)
{
    if (msg == 0)
        return;

Ale oczywiście nadal idzie do przodu i wywala, ponieważ msg nigdy nie jest null, gdy send_message jest wywołana.

Jak mówię, z całym kodem tak blisko siebie w trywialnym przykładzie, że łatwo znaleźć błąd. Jednak w rzeczywistych programach, z bardziej złożonymi relacjami między zmiennymi obiektami, które trzymają między sobą wskaźniki, łatwo jest popełnić błąd i trudno jest skonstruować niezbędne przypadki testowe do wykrycia błędu.

Proste rozwiązanie, w którym chcesz, aby funkcja mogła polegać na shared_ptr nadal nie jest null w całym, polega na tym, aby funkcja przydzielała swoją własną wartość true shared_ptr, zamiast polegać na w odniesieniu do istniejącego shared_ptr.

Minusem jest to, że kopiowanie shared_ptr nie jest wolne: nawet implementacje "lock-free" muszą używać operacji z blokadą, aby dotrzymać gwarancji gwintowania. Tak więc mogą być sytuacje, w których program może być znacznie przyspieszony przez zmianę shared_ptr na shared_ptr &. Ale nie jest to zmiana, którą można bezpiecznie wprowadzić do wszystkich programów. Zmienia to logiczne znaczenie programu.

Zauważ, że podobny błąd wystąpiłby, gdybyśmy użyli std::string przez zamiast std::shared_ptr<std::string>, a zamiast:

previous_message = 0;

Aby oczyścić wiadomość, powiedzieliśmy:

previous_message.clear();

Wtedy objawem byłoby przypadkowe wysłanie pustej wiadomości, zamiast nieokreślonego zachowania. Koszt dodatkowej kopii bardzo dużego ciągu może być znacznie bardziej znaczący niż koszt kopiowania shared_ptr, więc kompromis może być inny.

 105
Author: Daniel Earwicker,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-12-02 12:44:44

Stwierdziłem, że nie zgadzam się z najwyżej głosowaną odpowiedzią, więc poszedłem szukać eksperckich opinonów i oto one. Od http://channel9.msdn.com/Shows/Going+Deep/C-and-Beyond-2011-Scott-Andrei-and-Herb-Ask-Us-Anything

Herb Sutter: "kiedy przejdziesz shared_ptr' s, kopie są drogie "

Scott Meyers: "nie ma nic specjalnego w shared_ptr, jeśli chodzi o to, czy przekazujesz go przez wartość, czy przez odniesienie. Użyj dokładnie tej samej analizy, której używasz dla każdego inny typ zdefiniowany przez użytkownika. Ludzie wydają się mieć to postrzeganie, że shared_ptr w jakiś sposób rozwiązuje wszystkie problemy z zarządzaniem, a ponieważ jest mały, to koniecznie jest tani, aby przejść przez wartość. Trzeba go skopiować i wiąże się z tym koszt... jest to kosztowne, aby przekazać go według wartości, więc jeśli Mogę ujść na sucho z odpowiednią semantyką w moim programie, przekażę go przez odniesienie do const lub odniesienia zamiast"[3]}

Herb Sutter: "zawsze podawaj je przez odniesienie do const i bardzo czasami może dlatego, że wiesz, co nazwałeś może zmodyfikować rzecz, z której masz odniesienie, może wtedy możesz przekazać wartość... jeśli skopiujesz je jako parametry, o mój Boże, prawie nigdy nie musisz podbijać tej liczby referencji, ponieważ i tak jest utrzymywana przy życiu, i powinieneś przekazywać ją przez referencję, więc proszę zrób to" {]}

Update: Herb rozszerzył na ten temat tutaj: http://herbsutter.com/2013/06/05/gotw-91-solution-smart-pointer-parameters / , chociaż Morał z historia jest taka, że nie powinieneś w ogóle przekazywać shared_ptr", chyba że chcesz używać lub manipulować inteligentnym wskaźnikiem, takim jak udostępnianie lub przenoszenie własności."

 108
Author: Jon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-06-06 00:24:23

Odradzałbym tę praktykę, chyba że ty i inni programiści, z którymi pracujesz naprawdę, Naprawdę wiecie, co robicie.

Po pierwsze, nie masz pojęcia jak może ewoluować interfejs do twojej klasy i chcesz uniemożliwić innym programistom Robienie złych rzeczy. Podanie shared_ptr przez odniesienie nie jest czymś, czego programista powinien się spodziewać, ponieważ nie jest idiomatyczne, a to sprawia, że łatwo go używać nieprawidłowo. Program: Utwórz interfejs aby użyć nieprawidłowo. Przechodzenie przez odniesienie po prostu zaprosi problemy później.

Po drugie, nie Optymalizuj, dopóki nie wiesz, że ta konkretna klasa będzie problemem. Najpierw profil, a potem, jeśli twój program naprawdę potrzebuje impulsu podanego przez odniesienie, to może. W przeciwnym razie nie przejmuj się drobnymi rzeczami (tj. dodatkowymi N instrukcjami, których potrzeba, aby przekazać wartość) zamiast martwić się o projekt, struktury danych, algorytmy i długoterminową konserwację.

 22
Author: Mark Kegel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-11-29 16:32:32

Tak, biorąc odniesienia jest w porządku tam. Nie zamierzasz nadać metodzie współwłasności; ona chce tylko z nią pracować. Możesz też wziąć referencję do pierwszej sprawy, skoro i tak ją skopiujesz. Ale w pierwszym przypadku, to bierze własność. Jest taka sztuczka, aby skopiować ją tylko raz:

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

Należy również skopiować, gdy zwracasz go (tzn. nie zwracasz referencji). Ponieważ twoja klasa nie wie, co klient z nim robi (może przechowywać wskaźnik do niego i wtedy wydarzy się wielki wybuch). Jeśli później się okaże, że to wąskie gardło (Pierwszy profil!), wtedy nadal możesz zwrócić referencję.


Edit: oczywiście, jak podkreślają inni, jest to prawdą tylko wtedy, gdy znasz swój kod i wiesz, że nie zresetujesz przekazanego wskaźnika współdzielonego w jakiś sposób. Jeśli masz wątpliwości, po prostu podaj wartość.

 18
Author: Johannes Schaub - litb,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-11-30 13:49:24

Sensowne jest przechodzenie shared_ptr przez const&. Nie spowoduje to problemów (z wyjątkiem mało prawdopodobnego przypadku, gdy odwołana shared_ptr zostanie usunięta podczas wywołania funkcji, jak szczegółowo opisał Earwicker) i prawdopodobnie będzie szybsza, jeśli przekażesz wiele z nich. Zapamiętaj; domyślną wartością boost::shared_ptr jest thread safe, więc kopiowanie zawiera przyrost thread safe.

Spróbuj użyć const&, a nie tylko &, ponieważ obiekty tymczasowe nie mogą być przekazywane przez referencję non-const. (Chociaż język rozszerzenie w MSVC pozwala na to i tak)

 11
Author: Magnus Hoff,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-11-30 22:30:40

W drugim przypadku robienie tego jest prostsze:

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

Możesz to nazwać

only_work_with_sp(*sp);
 10
Author: Greg Rogers,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-11-29 16:21:54

Uniknąłbym" zwykłego " odniesienia, chyba że funkcja explicitely może zmodyfikować wskaźnik.

A const & może być sensowną mikro-optymalizacją podczas wywoływania małych funkcji - np. w celu umożliwienia dalszych optymalizacji, np. w celu usunięcia pewnych warunków. Ponadto inkrement/dekrement - ponieważ jest bezpieczny dla wątku-jest punktem synchronizacji. Nie spodziewałbym się jednak, że będzie to miało duże znaczenie w większości scenariuszy.

Ogólnie rzecz biorąc, powinieneś używać prostszego stylu, chyba że masz powód nie chcę. Następnie użyj const & konsekwentnie lub dodaj komentarz, dlaczego, jeśli używasz go tylko w kilku miejscach.

 3
Author: peterchen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-11-29 19:12:27

Zalecałbym przekazywanie współdzielonego wskaźnika przez const reference - semantykę, że funkcja przekazywana ze wskaźnikiem nie jest właścicielem wskaźnika, co jest czystym idiomem dla programistów.

Jedyną pułapką jest to, że w wielu programach wątkowych obiekt wskazywany przez współdzielony wskaźnik zostaje zniszczony w innym wątku. Można więc śmiało powiedzieć, że używanie const reference współdzielonego wskaźnika jest bezpieczne w programie jednowątkowym.

Przekazywanie współdzielonego wskaźnika przez referencję non-const jest czasami niebezpieczne-powodem są funkcje swap i reset, które funkcja może wywołać wewnątrz, aby zniszczyć obiekt, który jest nadal uważany za ważny po powrocie funkcji.

Nie chodzi chyba o przedwczesną optymalizację - chodzi o unikanie niepotrzebnego marnowania cykli procesora, gdy wiesz, co chcesz zrobić, a idiom kodowania został mocno przyjęty przez twoich kolegów programistów.

Tylko moje 2 grosze: -)

 3
Author: zmqiu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-03-28 19:37:28

Wydaje się, że wszystkie plusy i minusy tutaj można uogólnić na dowolny typ przekazany przez odniesienie, a nie tylko shared_ptr. Moim zdaniem powinieneś znać semantykę przechodzenia przez odniesienie, odniesienie const i wartość i używać go poprawnie. Ale nie ma absolutnie nic z natury złego w przekazywaniu shared_ptr przez odniesienie, chyba że uważasz, że wszystkie odniesienia są złe...

Aby wrócić do przykładu:

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

Skąd wiesz, że sp.do_something() nie wybuchnie z powodu zwisający wskaźnik?

Prawda jest taka, że shared_ptr lub nie, const lub nie, to może się zdarzyć, jeśli masz wadę projektu, jak bezpośrednie lub pośrednie dzielenie własności {[2] } między wątkami, pominięcie obiektu, który ma delete this, masz okrągłą własność lub inne błędy własności.

 3
Author: Sandy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-12-07 22:55:04

Jedna rzecz, o której jeszcze nie wspomniałem, to to, że kiedy przekazujesz współdzielone wskaźniki przez odniesienie, tracisz domyślną konwersję, którą otrzymujesz, jeśli chcesz przekazać pochodną dzielony wskaźnik klasy przez odniesienie do współdzielonego wskaźnika klasy bazowej.

Na przykład, ten kod spowoduje błąd, ale będzie działał, jeśli zmienisz test() tak, aby współdzielony wskaźnik nie był przekazywany przez odniesienie.

#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;
}
 2
Author: Malvineous,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-09-25 22:56:21

Zakładam, że jesteś zaznajomiony z przedwczesną optymalizacją i prosisz o to albo w celach akademickich, albo dlatego, że wyizolowałeś jakiś wcześniej istniejący kod, który jest niewystarczająco wydajny.

Przechodzenie przez odniesienie jest w porządku

Przechodzenie przez referencję const jest lepsze i zwykle może być używane, ponieważ nie wymusza zgodności na wskazywanym obiekcie.

Jesteś Nie ryzykujesz utratę WSKAŹNIKA z powodu użycia referencji. To odniesienie jest dowodem że masz kopię inteligentnego wskaźnika wcześniej w stosie i tylko jeden wątek posiada stos wywołań, tak że wcześniej istniejąca Kopia nie zniknie.

Używanie referencji jest często bardziej efektywne z wymienionych powodów, ale nie gwarantowane. Pamiętaj, że dereferencja obiektu również może wymagać pracy. Twój idealny scenariusz użycia odniesienia byłby taki, gdyby twój styl kodowania obejmował wiele małych funkcji, w których wskaźnik byłby przekazywany z funkcji do funkcji funkcja przed użyciem.

Powinieneś zawsze unikać przechowywania inteligentnego wskaźnika jako punktu odniesienia. Twój przykład Class::take_copy_of_sp(&sp) pokazuje poprawne użycie w tym celu.

 1
Author: Drew Dormann,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-11-30 16:00:36

Zakładając, że nie zajmujemy się poprawnością const (lub bardziej, masz na myśli umożliwienie funkcjom modyfikowania lub współdzielenia własności przekazywanych danych), przekazywanie wartości boost:: shared_ptr przez wartość jest bezpieczniejsze niż przekazywanie jej przez odniesienie, ponieważ pozwalamy oryginalnemu boost::shared_ptr kontrolować swój własny czas życia. Rozważmy wyniki poniższego kodu...

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 );
}

Z powyższego przykładu widzimy, że przekazanie sharedA przez referencję Pozwala FooTakesReference na zresetuj oryginalny wskaźnik, co zmniejsza jego liczbę użycia do 0, niszcząc jego dane. FooTakesValue , jednakże, nie może zresetować oryginalnego wskaźnika, gwarantując, że dane sharedB są nadal użyteczne. Kiedy pojawia się inny deweloper i próbuje wykorzystać kruchą egzystencję sharedy, następuje chaos. Jednak szczęśliwy programistasharedB wraca do domu wcześnie, ponieważ wszystko jest w jego świecie.

Bezpieczeństwo kodu, w tym przypadku, znacznie przewyższa prędkość poprawa kopiowanie tworzy. Jednocześnie boost:: shared_ptr ma na celu poprawę bezpieczeństwa kodu. Znacznie łatwiej będzie przejść od kopii do referencji, jeśli coś wymaga tego rodzaju optymalizacji niszowej.

 1
Author: Kit10,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-03-03 14:24:51

Sandy napisał (a): wydaje się, że wszystkie plusy i minusy tutaj można faktycznie uogólnić na dowolny typ przekazywany przez odniesienie, a nie tylko shared_ptr."

Prawda do pewnego stopnia, ale celem użycia shared_ptr jest wyeliminowanie obaw dotyczących życia obiektów i umożliwienie kompilatorowi obsługi tego za Ciebie. Jeśli chcesz przekazać współdzielony wskaźnik przez odniesienie i zezwolić klientom metod non-const, które mogą uwolnić dane obiektu, użyj współdzielonego wskaźnika to prawie bezcelowe.

Napisałem "prawie" w poprzednim zdaniu, ponieważ wydajność może być problemem, i to "może" być uzasadnione w rzadkich przypadkach, ale chciałbym również uniknąć tego scenariusza i szukać wszystkich możliwych innych rozwiązań optymalizacyjnych, takich jak poważnie spojrzeć na dodanie kolejnego poziomu indrection, leniwa ocena, itp..

Kod, który istnieje po jego autorze, a nawet po jego pamięci autora, który wymaga ukrytych założeń dotyczących zachowania, w szczególności zachowanie o życiu obiektu wymaga jasnej, zwięzłej, czytelnej dokumentacji, a wtedy wielu klientów i tak jej nie przeczyta! Prostota prawie zawsze przewyższa wydajność, a prawie zawsze są inne sposoby na efektywność. Jeśli naprawdę potrzebujesz przekazać wartości przez odniesienie, aby uniknąć głębokiego kopiowania przez konstruktory kopiujące obiektów z liczeniem odniesień( i operatora equals), to być może powinieneś rozważyć sposoby, aby głęboko skopiowane dane były wskaźnikami z liczeniem odniesień, które można skopiować szybko. (Oczywiście, to tylko jeden scenariusz projektowy, który może nie mieć zastosowania do twojej sytuacji).

 1
Author: Bill H,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-09-02 19:46:53

Kiedyś pracowałem w projekcie, że zasada była bardzo silna o przekazywaniu inteligentnych wskaźników przez wartość. Kiedy zostałem poproszony o wykonanie analizy wydajności-odkryłem, że dla zwiększania i zmniejszania liczników referencyjnych inteligentnych wskaźników aplikacja spędza między 4-6% czasu wykorzystywanego procesora.

Jeśli chcesz przekazać inteligentne wskaźniki według wartości, aby uniknąć problemów w dziwnych przypadkach opisanych przez Daniela Earwickera, upewnij się, że rozumiesz cenę, którą chcesz płacę za to.

Jeśli zdecydujesz się użyć referencji, głównym powodem użycia referencji const jest umożliwienie niejawnego upcastingu, gdy musisz przekazać współdzielony wskaźnik do obiektu z klasy, która dziedziczy klasę używaną w interfejsie.

 1
Author: gsf,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-04-19 18:43:03

Oprócz tego, co powiedział litb, chciałbym zaznaczyć, że prawdopodobnie przejdzie przez const reference w drugim przykładzie, w ten sposób masz pewność, że nie przypadkowo go zmodyfikować.

 0
Author: Leon Timmermans,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-11-29 17:36:21
struct A {
  shared_ptr<Message> msg;
  shared_ptr<Message> * ptr_msg;
}
  1. Pass by value:

    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. Pass by reference:

    void set(shared_ptr<Message>& msg) {
     this->msg = msg; /// reference count will be added, because reference is just an alias.
     }
    
  3. Podaj wskaźnik:

    void set(shared_ptr<Message>* msg) {
      this->ptr_msg = msg; /// reference count will not be added;
    }
    
 0
Author: Dylan Chen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-07-23 07:42:53

Każdy element kodu musi mieć jakiś sens. Jeśli przekazujesz współdzielony wskaźnik przez wartość wszędzie w aplikacji, oznacza to " nie jestem pewien, co się dzieje gdzie indziej, dlatego preferuję surowe Bezpieczeństwo ". To nie jest to, co nazywam dobrym znakiem zaufania dla innych programistów, którzy mogliby skonsultować kod.

W każdym razie, nawet jeśli funkcja otrzymuje referencję const i jesteś "niepewny", nadal możesz utworzyć kopię współdzielonego wskaźnika na czele funkcji, aby dodać silny odniesienie do wskaźnika. Może to być również postrzegane jako wskazówka o projekcie ("wskaźnik można zmodyfikować gdzie indziej").

Więc tak, IMO, domyślnie powinno być " pass by const reference ".

 0
Author: Philippe,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-03-05 07:45:33