Wskaźniki i referencje
Część sekcji Programowanie obiektowe ścieżki C++ w Coddy. Lekcja 13 z 104.
Skoro rozumiesz już pamięć stosu i sterty, poznajmy narzędzia, które pozwalają bezpośrednio pracować z lokalizacjami w pamięci: wskaźniki i referencje.
Wskaźnik przechowuje adres pamięci innej zmiennej. Deklarujesz go za pomocą *, a adres uzyskujesz za pomocą &.
int value = 42;
int* ptr = &value; // ptr przechowuje adres value
std::cout << ptr; // Wypisuje adres w pamięci
std::cout << *ptr; // Dereferencja: wypisuje 42Referencja jest aliasem istniejącej zmiennej. Po powiązaniu zawsze odnosi się do tej zmiennej. Deklarujesz ją za pomocą &.
int value = 42;
int& ref = value; // ref jest aliasem dla value
ref = 100; // Zmienia value na 100
std::cout << value; // Wypisuje 100Najważniejsze różnice:
| Wskaźniki | Referencje |
|---|---|
| Mogą mieć wartość null | Muszą zostać zainicjalizowane |
| Można je przypisać ponownie | Nie można ich ponownie powiązać |
Użyj *, aby uzyskać dostęp do wartości | Dostęp do wartości bezpośrednio |
| Mogą wskazywać na nic | Zawsze prawidłowe |
Oba są niezbędne do wydajnego przekazywania obiektów do funkcji bez kopiowania oraz do pracy z pamięcią przydzieloną na stercie. Referencje są bezpieczniejsze i prostsze, natomiast wskaźniki oferują większą elastyczność, gdy trzeba przypisać im nową wartość lub sprawdzić, czy są null.
Wyzwanie
ŁatwyZbudujmy prosty system modyfikowania wartości, który pokaże, jak wskaźniki i referencje działają inaczej podczas modyfikowania danych.
Utworzysz dwa pliki, aby uporządkować swój kod:
ValueOps.h: Zdefiniuj trzy funkcje, które modyfikują wartości całkowite na różne sposoby:doubleByPointer(int* ptr): przyjmuje wskaźnik i podwaja wartość, na którą on wskazujetripleByReference(int& ref): przyjmuje referencję i potraja wartośćswapValues(int* a, int* b): przyjmuje dwa wskaźniki i zamienia miejscami wartości, na które wskazują
main.cpp: Wczytaj dwie liczby całkowite, a następnie pokaż działanie wskaźników i referencji:- Wypisz oryginalne wartości:
"Original: <first> <second>" - Użyj
doubleByPointerdla pierwszej wartości, a następnie wypisz:"After double: <first>" - Użyj
tripleByReferencedla drugiej wartości, a następnie wypisz:"After triple: <second>" - Użyj
swapValues, aby zamienić miejscami obie wartości, a następnie wypisz:"After swap: <first> <second>"
- Wypisz oryginalne wartości:
Wywołując funkcje ze wskaźnikami, pamiętaj, aby użyć operatora pobrania adresu (&) i przekazać adres zmiennej. Referencje działają bardziej naturalnie — wystarczy przekazać zmienną bezpośrednio.
Dołącz plik nagłówkowy w main.cpp za pomocą #include "ValueOps.h".
Spróbuj swoich sił
#include <iostream>
#include "ValueOps.h"
using namespace std;
int main() {
// Wczytaj dwie liczby całkowite z wejścia
int first, second;
cin >> first;
cin >> second;
// Wypisz pierwotne wartości
cout << "Original: " << first << " " << second << endl;
// TODO: Użyj doubleByPointer dla pierwszej wartości (przekaż adres za pomocą &)
// TODO: Wypisz wartości po podwojeniu
// TODO: Użyj tripleByReference dla drugiej wartości (przekaż zmienną bezpośrednio)
// TODO: Wypisz wartości po potrojeniu
// TODO: Użyj swapValues, aby zamienić obie wartości miejscami (przekaż adresy za pomocą &)
// TODO: Wypisz wartości po zamianie miejscami
return 0;
}
Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.
Wszystkie lekcje w sekcji Programowanie obiektowe
1Podstawy programowania obiektowego
Pliki zewnętrzneBudowanie i kompilacja C++Pliki nagłówkowe i pliki źródłowePrzestrzenie nazw i zakresWprowadzenie do programowania obiektowego w C++Klasy a obiektyWskaźnik „this”Metody (funkcje składowe)Atrybuty (składowe danych)Podstawy konstruktorów i destruktorówPowtórzenie – prosty kalkulator4Właściwości klas
Elementy instancji a elementy statyczneGettery i setteryStałe funkcje składoweSłowo kluczowe mutableMetody i zmienne statyczneFunkcje i klasy zaprzyjaźnionePodsumowanie – menedżer konta bankowego7Dziedziczenie
Podstawy dziedziczeniaPoziomy dostępu w dziedziczeniuKolejność wywołań konstruktora i destruktoraNadpisywanie metodFunkcje wirtualne i VTableWielokrotne dziedziczenieDziedziczenie wirtualnePowtórzenie — hierarchia pracowników2Zarządzanie pamięcią
Pamięć stosu a stertyWskaźniki i referencjePamięć dynamiczna (new/delete)Inteligentne wskaźniki w C++RAII w C++Podsumowanie — menedżer tablicy dynamicznej5Hermetyzacja
Specyfikatory dostępu w C++Specyfikatory dostępu — szczegółowoUkrywanie informacjiStruktura a klasaKlasy zagnieżdżone i wewnętrznePodsumowanie — system ewidencji studentów8Polimorfizm
Polimorfizm czasu kompilacji i wykonaniaPrzeciążanie funkcjiFunkcje wirtualne — powtórkaCzysto wirtualne funkcjeKlasy abstrakcyjneProjektowanie interfejsów w C++Rzutowanie dynamiczne i RTTIPodsumowanie — kalkulator kształtów3Konstruktory i destruktory
Konstruktor domyślnyKonstruktor z parametramiKonstruktor kopiującyKonstruktor przenoszącyListy inicjalizacyjne konstruktoraKonstruktory delegująceDestruktor — szczegółowe omówienieZasada trzech / pięciu / zeraPodsumowanie — klasa String6Przeciążanie operatorów
Wprowadzenie do przeciążania operatorówPrzeciążanie operatorów arytmetycznychPrzeciążanie operatorów porównaniaOperatory strumieniowePrzeciążanie operatora przypisaniaPrzeciążanie operatorów [] i ()Operatory konwersji typówPodsumowanie — klasa Matrix9Szablony
Szablony funkcjiSzablony klasSpecjalizacja szablonówSzablony wariadycznePodstawy SFINAE i cech typówPodsumowanie — kontener generycznyPoćwicz samodzielnie: Kompilator C++ online