Jak widzę, inteligentne wskaźniki są szeroko stosowane w wielu rzeczywistych projektach C ++.
Chociaż niektóre inteligentne wskaźniki są oczywiście korzystne dla obsługi RAII i przeniesienia własności, istnieje również tendencja do domyślnego korzystania ze wskaźników wspólnych jako sposobu „wyrzucania elementów bezużytecznych” , aby programiści nie musieli tak dużo myśleć o alokacji .
Dlaczego współużytkowane wskaźniki są bardziej popularne niż integracja odpowiedniego urządzenia do usuwania śmieci, takiego jak Boehm GC ? (Czy w ogóle zgadzasz się, że są one bardziej popularne niż rzeczywiste GC?)
Wiem o dwóch zaletach tradycyjnych GC w porównaniu z liczeniem referencji:
- Konwencjonalne algorytmy GC nie mają problemu z cyklami odniesienia .
- Liczba referencyjna jest na ogół wolniejsza niż właściwy GC.
Jakie są powody używania inteligentnych wskaźników liczących referencje?
c++
garbage-collection
Miklós Homolya
źródło
źródło
std::unique_ptr
jest wystarczające i jako takie ma zerowe obciążenie ponad surowymi wskaźnikami pod względem wydajności w czasie wykonywania. Używającstd::shared_ptr
wszędzie, możesz również zaciemnić semantykę własności, tracąc jedną z głównych zalet inteligentnych wskaźników innych niż automatyczne zarządzanie zasobami - jasne zrozumienie intencji kodu.Odpowiedzi:
Niektóre zalety liczenia referencji w stosunku do odśmiecania:
Niskie koszty ogólne. Urządzenia do odśmiecania mogą być dość inwazyjne (np. Powodowanie, że program zawiesza się w nieprzewidywalnych momentach, gdy trwa proces cyklu odśmiecania) i dość intensywnie zajmują pamięć (np. Ślad pamięci procesu niepotrzebnie rośnie do wielu megabajtów, zanim w końcu rozpocznie się odśmiecanie)
Bardziej przewidywalne zachowanie. Dzięki liczeniu referencji masz gwarancję, że Twój obiekt zostanie uwolniony, gdy tylko ostatnie odniesienie do niego zniknie. Z drugiej strony, dzięki śmieciu, twój obiekt zostanie uwolniony „kiedyś”, kiedy system się do niego zbliży. W przypadku pamięci RAM zwykle nie jest to duży problem na komputerach stacjonarnych lub lekko obciążonych serwerach, ale w przypadku innych zasobów (np. Uchwytów plików) często trzeba je jak najszybciej zamknąć, aby uniknąć potencjalnych konfliktów później.
Prostsze Zliczanie referencji można wyjaśnić za kilka minut i zaimplementować za godzinę lub dwie. Śmieciarki, szczególnie te o przyzwoitej wydajności, są niezwykle złożone i niewiele osób je rozumie.
Standard. C ++ obejmuje liczenie referencji (przez shared_ptr) i znajomych w STL, co oznacza, że większość programistów C ++ zna go i większość kodu C ++ będzie z nim współpracować. Nie ma jednak żadnego standardowego śmieciarza w języku C ++, co oznacza, że musisz wybrać jeden i mieć nadzieję, że będzie on działał dobrze w twoim przypadku użycia - a jeśli nie, problem jest naprawiony, a nie język.
Jeśli chodzi o rzekome wady liczenia referencji - niewykrywanie cykli to problem, ale taki, którego nigdy osobiście nie spotkałem w ciągu ostatnich dziesięciu lat korzystania z liczenia referencji. Większość struktur danych ma naturalnie acykliczny charakter, a jeśli natrafisz na sytuację, w której potrzebujesz cyklicznych referencji (np. Wskaźnik nadrzędny w węźle drzewa), możesz po prostu użyć słaby_ptr lub surowego wskaźnika C dla „kierunku wstecz”. Dopóki masz świadomość potencjalnego problemu podczas projektowania struktur danych, nie stanowi to problemu.
Jeśli chodzi o wydajność, nigdy nie miałem problemu z wydajnością liczenia referencji. Miałem problemy z wydajnością wyrzucania elementów bezużytecznych, w szczególności z przypadkowymi zamrożeniami, które może powodować GC, do których jedyne rozwiązanie („nie przydzielaj obiektów”) może równie dobrze zostać przeformułowane, jak „nie używaj GC” .
źródło
make_shared
powracasz. Mimo to opóźnienia są zwykle większym problemem w aplikacjach czasu rzeczywistego, ale przepustowość jest ogólnie ważniejsza, dlatego śledzenie GC jest tak szeroko stosowane. Nie tak szybko mówiłbym o nich źle.Aby uzyskać dobrą wydajność z GC, GC musi być w stanie przenosić obiekty w pamięci. W języku takim jak C ++, w którym można bezpośrednio oddziaływać na lokalizacje pamięci, jest to prawie niemożliwe. (Microsoft C ++ / CLR się nie liczy, ponieważ wprowadza nową składnię wskaźników zarządzanych przez GC, a zatem jest efektywnie innym językiem.)
Boehm GC, choć sprytny pomysł, jest w rzeczywistości najgorszy z obu światów: potrzebujesz malloc (), który jest wolniejszy niż dobry GC, a więc tracisz deterministyczne zachowanie alokacji / dezalokacji bez odpowiedniego wzrostu wydajności generacyjnego GC . Ponadto z konieczności jest konserwatywny, więc i tak niekoniecznie zbierze wszystkie śmieci.
Dobry, dobrze dostrojony GC może być świetną rzeczą. Ale w języku takim jak C ++ zyski są minimalne, a koszty często po prostu nie są tego warte.
Ciekawe będzie jednak, gdy C ++ 11 stanie się bardziej popularny, czy lambdas i semantyka przechwytywania zaczną prowadzić społeczność C ++ w kierunku tego samego rodzaju problemów związanych z alokacją i życiem obiektów, które spowodowały, że społeczność Lisp wymyśliła GC w pierwszym miejsce.
Zobacz także moją odpowiedź na powiązane pytanie dotyczące StackOverflow .
źródło
To prawda, ale obiektywnie większość kodu jest teraz pisana w nowoczesnych językach ze śledzeniem modułów zbierających śmieci.
To zły pomysł, ponieważ nadal musisz martwić się o cykle.
Och, wow, w twoim sposobie myślenia jest tak wiele rzeczy:
GC Boehm'a nie jest „właściwym” GC w żadnym znaczeniu tego słowa. To jest naprawdę okropne. Jest konserwatywny, więc przecieka i jest nieefektywny z założenia. Zobacz: http://flyingfrogblog.blogspot.co.uk/search/label/boehm
Wskaźniki udostępnione, obiektywnie, nie są tak popularne jak GC, ponieważ ogromna większość programistów używa teraz języków GC i nie potrzebuje wspólnych wskaźników. Wystarczy spojrzeć na Java i JavaScript na rynku pracy w porównaniu do C ++.
Wydaje się, że ograniczasz się do C ++, ponieważ, jak zakładam, uważasz, że GC jest kwestią styczną. To nie jest ( jedynym sposobem na uzyskanie przyzwoitej GC jest zaprojektowanie dla niej języka i VM od samego początku), więc wprowadzasz błąd selekcji. Ludzie, którzy naprawdę chcą właściwego zbierania śmieci, nie trzymają się C ++.
Jesteś ograniczony do C ++, ale chciałbyś mieć automatyczne zarządzanie pamięcią.
źródło
W MacOS X i iOS, a także u programistów używających Objective-C lub Swift, liczenie referencji jest popularne, ponieważ jest obsługiwane automatycznie, a użycie funkcji zbierania śmieci znacznie spadło, ponieważ Apple już go nie obsługuje (słyszę, że aplikacje używające wyrzucanie elementów bezużytecznych ulegnie awarii w następnej wersji systemu MacOS X, a odśmiecanie nigdy nie zostało zaimplementowane w systemie iOS). Naprawdę poważnie wątpię, czy kiedykolwiek było dużo oprogramowania korzystającego ze śmiecia, gdy było ono dostępne.
Powód pozbycia się śmieci: Nigdy nie działał niezawodnie w środowisku typu C, w którym wskaźniki mogły „uciekać” do obszarów niedostępnych dla śmieciarza. Apple mocno wierzy i wierzy, że liczenie referencji jest szybsze. (Możesz tutaj wysuwać roszczenia dotyczące względnej prędkości, ale nikt nie był w stanie przekonać Apple'a). I w końcu nikt nie używał śmieci.
Pierwszą rzeczą, której uczy się każdy programista MacOS X lub iOS, jest sposób obsługi cykli referencyjnych, więc nie jest to problem dla prawdziwego programisty.
źródło
Największą wadą wyrzucania elementów bezużytecznych w C ++ jest to, że po prostu niemożliwe jest poprawne:
W C ++ wskaźniki nie żyją we własnej społeczności o ścianach, są mieszane z innymi danymi. W związku z tym nie można odróżnić wskaźnika od innych danych, które akurat mają wzór bitowy, który można interpretować jako prawidłowy wskaźnik.
Konsekwencja: Dowolny moduł czyszczący C ++ wycieknie obiekty, które powinny zostać zebrane.
W C ++ można wykonywać arytmetykę wskaźników, aby uzyskać wskaźniki. Jako taki, jeśli nie znajdziesz wskaźnika na początku bloku, nie oznacza to, że do tego bloku nie można się odwoływać.
Konsekwencja: Każdy śmieciarz C ++ musi wziąć pod uwagę te zmiany, traktując każdą sekwencję bitów, która zdarzy się wskazywać w dowolnym miejscu bloku, w tym tuż po jego końcu, jako prawidłowy wskaźnik, który odwołuje się do bloku.
Uwaga: Żaden śmieciarz C ++ nie obsługuje kodu za pomocą takich sztuczek:
To prawda, że wywołuje to niezdefiniowane zachowanie. Ale część istniejącego kodu jest bardziej sprytna niż jest dla niego dobra i może spowodować wstępne zwolnienie przez moduł odśmiecający.
źródło