Historia pytania:
Wskaźniki to jeden z podstawowych narzędzi w C++ od momentu powstania języka, podstawą do zarządzania pamięcią i dynamicznymi strukturami, a także interakcji z niskopoziomowymi bibliotekami systemowymi. Referencje zostały dodane później (wraz z powstaniem samego C++) w celu uproszczenia składni i zwiększenia bezpieczeństwa kodu.
Problem:
Błędy w pracy z pamięcią są jednym z najczęstszych problemów w C++, szczególnie przy niewłaściwym użyciu wskaźników (dangling pointers, memory leaks). Czasami początkujący nie rozumie, w jakich przypadkach używać referencji, a w jakich wskaźników, lub błędnie uważa, że są one w pełni zamienne.
Rozwiązanie:
Wskaźnik (T*) to zmienna, która przechowuje adres obiektu, może być nullptr, obsługuje arytmetykę, umożliwia dynamiczne przydzielanie i usuwanie, a także może zmieniać, na co wskazuje.
Referencja (T&) to alias innego istniejącego obiektu. Referencje muszą być inicjalizowane przy tworzeniu, nie mogą być „puste” (referencing null), nie wspierają arytmetyki, ale są wygodniejsze i bezpieczniejsze (na przykład podczas przekazywania do funkcji).
Przykład kodu:
void increment_pointer(int* p) { if(p) ++(*p); } void increment_reference(int& r) { ++r; } int main() { int a = 5; increment_pointer(&a); increment_reference(a); }
Kluczowe cechy:
Czy można zmienić referencję po jej inicjalizacji, aby wskazywała na inny obiekt?
Nie. Referencja w C++ jest stałym aliasem. Po inicjalizacji referencja zawsze będzie wskazywała na ten sam obiekt, w odróżnieniu od wskaźnika, który można przekierować.
Przykład kodu:
int a = 1; int b = 2; int& ref = a; // ref = b - przypisuje b wartość, ale ref nadal wskazuje na a
Czy można stworzyć „zerową” referencję lub referencję do nullptr?
Nie, standard nie dopuszcza referencji do nieistniejących obiektów. Prowadzi to do nieokreślonego zachowania.
Przykład:
int* p = nullptr; // int& r = *p; - UB
Czym różni się int const ptr od const int ptr?**
int* const ptr to stały wskaźnik do int (wskaźnika nie można przekierowywać, ale wartość można zmieniać). const int* ptr to wskaźnik do stałego int (nie można zmieniać zawartości, na którą wskazuje, ale wskaźnik można przekierowywać).
Przykład kodu:
int a = 1, b = 2; const int* ptr1 = &a; // *ptr1 nie można zmieniać, ptr1 = &b - można int* const ptr2 = &a; // *ptr2 można zmieniać, ptr2 = &b - nie można
Młody programista używał wskaźników do przekazywania parametrów, nie sprawdzał ich na nullptr, co prowadziło do awaryjnych zakończeń przy błędach.
Zalety:
Wady:
Doświadczony programista używał int&, gdy przekazanie było gwarantowane, a int*, gdy możliwy był nullptr, zawsze sprawdzając wskaźnik na null.
Zalety:
Wady: