Pozwól mi też spróbować odpowiedzieć na to pytanie.
Wskaźniki są podobne do referencji. Innymi słowy, nie są to kopie, ale raczej sposób na odniesienie do oryginalnej wartości.
Przede wszystkim jednym z miejsc, w których zwykle będziesz musiał często korzystać ze wskaźników, jest, gdy masz do czynienia z wbudowanym sprzętem . Być może musisz zmienić stan cyfrowego PIN-u IO. Być może przetwarzasz przerwanie i musisz zapisać wartość w określonej lokalizacji. Dostajesz obraz. Jeśli jednak nie masz do czynienia ze sprzętem i zastanawiasz się, jakiego rodzaju użyć, czytaj dalej.
Po co używać wskaźników zamiast normalnych zmiennych? Odpowiedź staje się jaśniejsza, gdy masz do czynienia ze złożonymi typami, takimi jak klasy, struktury i tablice. Jeśli użyjesz normalnej zmiennej, możesz zrobić kopię (kompilatory są wystarczająco inteligentne, aby temu zapobiec w niektórych sytuacjach, a C ++ 11 też pomaga, ale na razie nie będziemy omijać tej dyskusji).
Co się stanie, jeśli chcesz zmodyfikować oryginalną wartość? Możesz użyć czegoś takiego:
MyType a; //let's ignore what MyType actually is right now.
a = modify(a);
To zadziała dobrze, a jeśli nie wiesz dokładnie, dlaczego używasz wskaźników, nie powinieneś ich używać. Uważaj na powód „prawdopodobnie szybszy”. Uruchom własne testy, a jeśli faktycznie są szybsze, użyj ich.
Powiedzmy jednak, że rozwiązujesz problem, w którym musisz przydzielić pamięć. Przydzielając pamięć, musisz ją cofnąć. Przydział pamięci może, ale nie musi, być udany. Tutaj przydają się wskaźniki - pozwalają przetestować istnienie przydzielonego obiektu i pozwalają uzyskać dostęp do obiektu, dla którego przydzielono pamięć, usuwając odwołanie ze wskaźnika.
MyType *p = NULL; //empty pointer
if(p)
{
//we never reach here, because the pointer points to nothing
}
//now, let's allocate some memory
p = new MyType[50000];
if(p) //if the memory was allocated, this test will pass
{
//we can do something with our allocated array
for(size_t i=0; i!=50000; i++)
{
MyType &v = *(p+i); //get a reference to the ith object
//do something with it
//...
}
delete[] p; //we're done. de-allocate the memory
}
To jest klucz do tego, dlaczego miałbyś używać wskaźników - referencje zakładają, że element, do którego się odwołujesz, już istnieje . Wskaźnik nie.
Innym powodem, dla którego używałbyś wskaźników (a przynajmniej musiałbyś sobie z nimi poradzić) jest to, że są one typem danych, który istniał przed referencjami. Dlatego jeśli w końcu użyjesz bibliotek do robienia rzeczy, w których wiesz, że są lepsze, przekonasz się, że wiele z tych bibliotek używa wskaźników w dowolnym miejscu, po prostu ze względu na to, jak długo są w pobliżu (dużo z nich zostały napisane przed C ++).
Jeśli nie korzystałeś z żadnych bibliotek, możesz zaprojektować swój kod w taki sposób, aby trzymać się z dala od wskaźników, ale biorąc pod uwagę, że wskaźniki są jednym z podstawowych typów języka, im szybciej będziesz z nich wygodnie korzystać, tym bardziej przenośne byłyby twoje umiejętności C ++.
Z punktu widzenia łatwości konserwacji, powinienem również wspomnieć, że kiedy korzystasz ze wskaźników, musisz albo sprawdzić ich ważność i zająć się sprawą, gdy są one nieważne, lub po prostu założyć, że są one prawidłowe i zaakceptować fakt, że twój program ulegnie awarii lub gorzej, GDY założenie zostanie złamane. Innymi słowy, wybór wskaźników polega na wprowadzeniu złożoności kodu lub większego nakładu prac konserwacyjnych, gdy coś się zepsuje i próbujesz wyśledzić błąd, który należy do całej klasy błędów wprowadzanych przez wskaźniki, takich jak uszkodzenie pamięci.
Więc jeśli kontrolujesz cały swój kod, trzymaj się z dala od wskaźników i zamiast tego używaj referencji, utrzymując je na stałe, kiedy możesz. Zmusi cię to do zastanowienia się nad czasem życia twoich obiektów i sprawi, że twój kod będzie łatwiejszy do zrozumienia.
Pamiętaj tylko o tej różnicy: Odwołanie jest zasadniczo poprawnym wskaźnikiem. Wskaźnik nie zawsze jest prawidłowy.
Czy więc mówię, że niemożliwe jest utworzenie nieprawidłowego odwołania? Nie. Jest to całkowicie możliwe, ponieważ C ++ pozwala robić prawie wszystko. Trudniej jest robić to niezamierzenie, a będziesz zaskoczony, jak wiele błędów jest niezamierzonych :)