Czytałem skądś, że podczas używania C ++ zaleca się nie używać wskaźników. Dlaczego wskaźniki są tak złym pomysłem, gdy używasz C ++. Dla programistów C przyzwyczajonych do używania wskaźników, jaka jest lepsza alternatywa i podejście w C ++?
45
NetConnection
instancji rozłączających się z serwerem ( stackoverflow.com/questions/14780456/... ), a także problem z występowaniem wielu obiektów w programie, których konkretnie odmawia gromadzenia ...GCRoots are never garbage collected.
i akapit zacznij odThe MMgc is considered a conservative collector for mark/sweep.
). Z technicznego punktu widzenia jest to problem w programie Adobe Virtual Machine 2, a nie w samym AS3, ale gdy masz takie problemy w językach wyższego poziomu, które mają wbudowane funkcje czyszczenia pamięci, często nie masz żadnego prawdziwego sposobu na debugowanie w języku te problemy całkowicie poza programem. ...Odpowiedzi:
Myślę, że oznaczają, że powinieneś używać inteligentnych wskaźników zamiast zwykłych wskaźników.
W C ++ nacisk kładziony byłby na wyrzucanie elementów bezużytecznych i zapobieganie wyciekom pamięci (żeby wymienić tylko dwa). Wskaźniki są podstawową częścią języka, więc nieużywanie ich jest prawie niemożliwe, z wyjątkiem najbardziej trywialnych programów.
źródło
Ponieważ jestem tym, który opublikował polemikę „nie używaj wskazówek, kurwa” , czuję, że powinienem tutaj skomentować.
Po pierwsze, jako polemika, reprezentuje oczywiście skrajny punkt widzenia. Tam są zdecydowanie uzasadnione zastosowania (RAW) wskaźniki. Ale ja (i wielu profesjonalnych programistów C ++) twierdzę, że przypadki te są niezwykle rzadkie. Ale tak naprawdę mamy na myśli:
Pierwszy:
Tutaj „własna pamięć” zasadniczo oznacza, że w pewnym momencie
delete
wywoływany jest ten wskaźnik (ale jest on bardziej ogólny). To stwierdzenie można bezpiecznie uznać za absolutne. Jedyny wyjątek to podczas wdrażania własnego inteligentnego wskaźnika (lub innych strategii zarządzania pamięci). I nawet tam zwykle powinieneś nadal używać inteligentnego wskaźnika na niskim poziomie.Uzasadnienie tego jest dość proste: surowe wskaźniki, których własna pamięć wprowadza źródło błędu. Błędy te są liczne w istniejącym oprogramowaniu: wycieki pamięci i podwójne usuwanie - oba są bezpośrednią konsekwencją niejasnego posiadania zasobów (ale idą w przeciwnym kierunku).
Problem ten można całkowicie wyeliminować, praktycznie bez żadnych kosztów, po prostu używając inteligentnych wskaźników zamiast wskaźników surowych (zastrzeżenie: to oczywiście wymaga myślenia, oczywiście; wspólne wskaźniki mogą prowadzić do cykli, a tym samym do wycieków pamięci - ale jest to łatwe do uniknięcia).
Druga:
W przeciwieństwie do innych języków, C ++ ma bardzo silne wsparcie dla semantyki wartości i po prostu nie potrzebuje pośrednictwa wskaźników. Nie zostało to natychmiast zrealizowane - historycznie C ++ został wymyślony w celu ułatwienia łatwej orientacji obiektów w C i polegał w dużej mierze na tworzeniu grafów obiektowych połączonych wskaźnikami. Ale we współczesnym C ++ ten paradygmat rzadko jest najlepszym wyborem, a współczesne idiomy C ++ często nie potrzebują wskaźników . Działają na wartościach, a nie na wskaźnikach.
Niestety, ta wiadomość wciąż nie została przyjęta w dużej części społeczności użytkowników C ++. W rezultacie większość napisanego kodu C ++ jest wciąż wypełniona zbędnymi wskaźnikami, które sprawiają, że kod jest złożony, powolny i wadliwy / zawodny.
Dla kogoś, kto zna nowoczesnych C ++, to jasne, że bardzo rzadko trzeba żadnych wskazówek (zarówno inteligentne lub nieprzetworzone, z wyjątkiem gdy wykorzystując je jako iteratory). Powstały kod jest krótszy, mniej złożony, bardziej czytelny, często bardziej wydajny i bardziej niezawodny.
źródło
std::unique_ptr
. A czemu nieptr_vec
? Ale zazwyczaj wektor wartości z nadal będzie zamieniał się szybciej (szczególnie w przypadku semantyki ruchu).boost::variant
zrecursive_wrapper
jest prawdopodobnie moim ulubionym rozwiązaniem do reprezentowania DAG.Po prostu dlatego, że masz do dyspozycji abstrakcje, które ukrywają bardziej temperamentne aspekty używania wskaźników, takie jak dostęp do surowej pamięci i sprzątanie po twoich przydziałach. Dzięki inteligentnym wskaźnikom, klasom kontenerów i wzorcom projektowym, takim jak RAII, potrzeba korzystania z surowych wskaźników jest zmniejszona. To powiedziawszy, jak każda abstrakcja, powinieneś zrozumieć, jak one faktycznie działają, zanim przejdziesz dalej.
źródło
Stosunkowo po prostu mentalność C brzmi „Masz problem? Użyj wskaźnika”. Możesz to zobaczyć w ciągach C, wskaźnikach funkcji, wskaźnikach jako iteratorach, wskaźnikach do wskaźników, wskaźnikach pustych - nawet na początku C ++ ze wskaźnikami składowymi.
Ale w C ++ możesz użyć wartości do wielu lub wszystkich tych zadań. Potrzebujesz abstrakcji funkcji?
std::function
. Jest to wartość, która jest funkcją.std::string
? To wartość, to ciąg. Podobne podejścia można zobaczyć w całym C ++. Dzięki temu analiza kodu jest znacznie łatwiejsza zarówno dla ludzi, jak i kompilatorów.źródło
Jednym z powodów jest zbyt szerokie zastosowanie wskaźników. Można ich używać do iteracji po kontenerach, do unikania kopiowania dużych obiektów podczas przechodzenia do funkcji, niebanalnego zarządzania czasem życia, dostępu do losowych miejsc w pamięci itp. A gdy użyjesz ich do jednego celu, inne ich funkcje stają się dostępne natychmiast niezależnie od zamiaru.
Wybór narzędzia do konkretnego celu sprawia, że kod jest prostszy i bardziej czytelny - iteratory iteracji, inteligentne wskaźniki do zarządzania czasem życia itp.
źródło
Poza wymienionymi już przyczynami jest oczywisty: lepsze optymalizacje. Analiza aliasingu jest zdecydowanie zbyt skomplikowana pod względem arytmetyki wskaźnika, podczas gdy referencje wskazują optymalizator, więc o wiele głębsza analiza aliasingu jest możliwa, jeśli używane są tylko referencje.
źródło
Oprócz ryzyka wycieków pamięci wskazanego przez wskaźnik @jmquigley i arytmetykę wskaźnika można uznać za problematyczne, ponieważ wskaźniki mogą wskazywać wszędzie w pamięci, powodując „trudne do znalezienia błędy” i „luki w zabezpieczeniach”.
Właśnie dlatego zostali prawie porzuceni na C # i Javie.
źródło
unsafe
słowo kluczoweC ++ obsługuje większość C , funkcji oraz Obiekty i Klasy. C miał już wskaźniki i inne rzeczy.
Wskaźniki są bardzo przydatną techniką, którą można łączyć z Object Orientation, a C ++ je obsługuje. Ale ta technika jest trudna do nauczenia i trudna do zrozumienia, a jej bardzo łatwo jest powodować niepożądane błędy.
Wiele nowych języków programowania udaje, że nie używa wskaźników ze obiektami, takich jak Java, .NET, Delphi, Vala, PHP, Scala. Ale wskaźniki są nadal używane „za kulisami”. Te techniki „ukrytego wskaźnika” są nazywane „referencjami”.
W każdym razie uważam wskaźnik (i) za wzorzec programowania, jako prawidłowy sposób rozwiązywania niektórych problemów, podobnie jak programowanie obiektowe .
Inni programiści mogą mieć inne zdanie. Ale proponuję, aby uczniowie i programiści dowiedzieli się, jak:
(1) Używaj wskaźników bez obiektów
(2) obiekty bez wskaźników
(3) wyraźne wskaźniki do obiektów
(4) „ukryte” wskaźniki do obiektów ( odniesienie AKA ) ;-)
W tej kolejności.
Nawet jeśli jest trudny do nauczenia i trudny do nauczenia. Do
C++
tych celów można użyć Object Pascal (Delphi, FreePascal, inne) i (nie Java ani C #).Później nowi programiści mogą przejść do języków programowania „ukryte wskaźniki do obiektów”, takich jak: Java, C #, PHP zorientowany obiektowo i inne.
źródło
Mówiąc o VC6, kiedy rzutujesz wskaźnik klasy (tworzony przez instancję) na zmienną (np. DWORD), nawet jeśli ten wskaźnik jest lokalny, możesz uzyskać dostęp do klasy przez wszystkie funkcje, które używają tej samej sterty. Instancja klasy jest zdefiniowana jako lokalna, ale w rzeczywistości tak nie jest. O ile mi wiadomo, każdy adres zmiennej, struktury lub klasy sterty jest unikalny przez cały okres istnienia klasy hostującej.
Przykład:
EDYCJA To bardzo mała część oryginalnego kodu. Klasa CSRecodset to tylko klasa rzutowania CXdbRecordset, w której znajduje się cały prawdziwy kod. W ten sposób mogę pozwolić użytkownikowi skorzystać z tego, co napisałem, bez utraty moich praw. Nie udaję, że demonstruję, że mój silnik bazy danych jest profesjonalny, ale naprawdę działa.
EDYCJA: wymagane przez DeadMG:
źródło
DWORD
jest obraźliwa i być może niepoprawna (DWORD niekoniecznie jest wystarczająco szeroki, aby pomieścić wskaźnik). Jeśli potrzebujesz nietypowego wskaźnika, skorzystajvoid*
- ale kiedy potrzebujesz go w C ++, często masz problem z projektem w kodzie, który powinieneś naprawić.