Stroustrup skomentował to na konferencji Going Native 2013.
Wystarczy przejść do około 25m50 w tym filmie . (Właściwie polecam obejrzenie całego filmu, ale przechodzę do rzeczy związanych z odśmiecaniem).
Gdy masz naprawdę świetny język, który ułatwia (i jest bezpieczny, przewidywalny, łatwy do odczytania i łatwy do nauczenia) zajmowanie się przedmiotami i wartościami w bezpośredni sposób, unikając (jawnego) użycia sterty, to nawet nie chcesz śmieci.
W nowoczesnym C ++ oraz w C ++ 11 odśmiecanie nie jest już pożądane, z wyjątkiem ograniczonych okoliczności. W rzeczywistości, nawet jeśli dobry śmieciarz jest wbudowany w jeden z głównych kompilatorów C ++, myślę, że nie będzie on używany zbyt często. Będzie to łatwiejsze , nie ciężej, aby uniknąć GC.
Pokazuje ten przykład:
void f(int n, int x) {
Gadget *p = new Gadget{n};
if(x<100) throw SomeException{};
if(x<200) return;
delete p;
}
Jest to niebezpieczne w C ++. Ale w Javie jest to również niebezpieczne! W C ++, jeśli funkcja zwraca wcześniej, delete
nigdy nie zostanie wywołana. Ale jeśli masz pełne wyrzucanie elementów bezużytecznych, na przykład w Javie, otrzymujesz jedynie sugestię, że obiekt zostanie zniszczony „w pewnym momencie w przyszłości” ( aktualizacja: jest nawet gorzej niż to. Java robi nieobiecaj, że zadzwonisz do finalizatora kiedykolwiek - może nigdy nie zostanie wywołany). Nie jest to wystarczające, jeśli gadżet posiada otwarty uchwyt pliku, połączenie z bazą danych lub dane, które zostały później buforowane do zapisu w bazie danych. Chcemy, aby gadżet został zniszczony, gdy tylko się skończy, aby jak najszybciej uwolnić te zasoby. Nie chcesz, aby Twój serwer bazy danych borykał się z tysiącami połączeń z bazą danych, które nie są już potrzebne - nie wie, że twój program jest gotowy do pracy.
Więc jakie jest rozwiązanie? Istnieje kilka podejść. Oczywistym podejściem, które zastosujesz w zdecydowanej większości swoich obiektów, jest:
void f(int n, int x) {
Gadget p = {n}; // Just leave it on the stack (where it belongs!)
if(x<100) throw SomeException{};
if(x<200) return;
}
Pisanie zajmuje mniej znaków. Nie przeszkadza new
. Nie wymaga Gadget
dwukrotnego pisania . Obiekt jest niszczony na końcu funkcji. Jeśli tego właśnie chcesz, jest to bardzo intuicyjne. Gadget
zachowują się tak samo jak int
lub double
. Przewidywalny, łatwy do odczytania, łatwy do nauczenia. Wszystko jest „wartością”. Czasami duża wartość, ale wartości są łatwiejsze do nauczenia, ponieważ nie masz tej „akcji na odległość”, którą otrzymujesz ze wskaźnikami (lub referencjami).
Większość tworzonych obiektów jest wykorzystywana tylko w funkcji, która je utworzyła, i być może przekazywana jako dane wejściowe do funkcji potomnych. Programiści nie powinni myśleć o „zarządzaniu pamięcią” podczas zwracania obiektów lub w inny sposób udostępniania obiektów w szeroko oddzielnych częściach oprogramowania.
Ważny jest zakres i czas życia. W większości przypadków łatwiej jest, jeśli czas życia jest taki sam jak zakres. Łatwiej to zrozumieć i łatwiej uczyć. Jeśli chcesz mieć inny czas życia, powinno być oczywiste, że czytasz kod, który robisz, shared_ptr
na przykład za pomocą. (Lub zwracając (duże) obiekty według wartości, wykorzystując semantykę move lub unique_ptr
.
Może to wydawać się problemem wydajności. Co jeśli chcę zwrócić gadżet foo()
? Semantyka ruchów w C ++ 11 ułatwia zwracanie dużych obiektów. Po prostu napisz, Gadget foo() { ... }
a będzie działać i działać szybko. Nie musisz się z &&
sobą bawić, po prostu zwracaj wartości według wartości, a język często będzie w stanie dokonać niezbędnych optymalizacji. (Nawet przed C ++ 03 kompilatory wykonały wyjątkowo dobrą robotę, unikając niepotrzebnego kopiowania.)
Jak Stroustrup powiedział w innym miejscu filmu (parafrazując): „Tylko informatyk nalegałby na skopiowanie obiektu, a następnie zniszczenie oryginału. (Śmiech publiczności). Dlaczego nie przenieść obiektu bezpośrednio w nowe miejsce? To właśnie ludzie (nie informatycy) oczekują ”.
Gdy możesz zagwarantować, że potrzebna jest tylko jedna kopia obiektu, o wiele łatwiej jest zrozumieć jego żywotność. Możesz wybrać, jakie zasady na całe życie chcesz, a usuwanie śmieci jest dostępne, jeśli chcesz. Ale kiedy zrozumiesz zalety innych podejść, zauważysz, że wyrzucanie elementów bezużytecznych znajduje się na dole listy preferencji.
Jeśli to nie zadziała, można użyć unique_ptr
, lub w przypadku jego braku, shared_ptr
. Dobrze napisany C ++ 11 jest krótszy, łatwiejszy do odczytania i łatwiejszy do nauczenia niż wiele innych języków, jeśli chodzi o zarządzanie pamięcią.