Wybór członków dla struktur i odniesień do struktur
W lekcji 13.7 — Wprowadzenie do struktur, elementów i wyboru elementów, pokazaliśmy, że możesz użyć operatora wyboru członków (.) aby wybrać element z obiektu struktury:
#include <iostream>
struct Employee
{
int id {};
int age {};
double wage {};
};
int main()
{
Employee joe { 1, 34, 65000.0 };
// Użyj operatora wyboru elementu członkowskiego (.), aby wybrać element z obiektu struktury
++joe.age; // Joe miał urodziny
joe.wage = 68000.0; // Joe dostał awans
return 0;
}Ponieważ odniesienia do obiektu działają tak samo jak sam obiekt, możemy również użyć operatora wyboru elementu (.) aby wybrać element z odniesienia do struktury:
#include <iostream>
struct Employee
{
int id{};
int age{};
double wage{};
};
void printEmployee(const Employee& e)
{
// Użyj operatora wyboru elementu członkowskiego (.), aby wybrać element z odniesienia do struktury
std::cout << "Id: " << e.id << '\n';
std::cout << "Age: " << e.age << '\n';
std::cout << "Wage: " << e.wage << '\n';
}
int main()
{
Employee joe{ 1, 34, 65000.0 };
++joe.age;
joe.wage = 68000.0;
printEmployee(joe);
return 0;
}Wybór elementu dla wskaźników do struktur
Jednakże operatora wyboru elementu (.) nie można użyć bezpośrednio na wskaźniku do struktury struct:
#include <iostream>
struct Employee
{
int id{};
int age{};
double wage{};
};
int main()
{
Employee joe{ 1, 34, 65000.0 };
++joe.age;
joe.wage = 68000.0;
Employee* ptr{ &joe };
std::cout << ptr.id << '\n'; // Błąd kompilacji: nie można użyć operatora. ze wskaźnikami
return 0;
}Za pomocą normalnych zmiennych lub referencji możemy uzyskać bezpośredni dostęp do obiektów. Ponieważ jednak wskaźniki przechowują adresy, musimy najpierw wyłuskać wskaźnik, aby uzyskać obiekt, zanim będziemy mogli cokolwiek z nim zrobić. Zatem jeden ze sposobów uzyskania dostępu do elementu członkowskiego ze wskaźnika do struktury jest następujący:
#include <iostream>
struct Employee
{
int id{};
int age{};
double wage{};
};
int main()
{
Employee joe{ 1, 34, 65000.0 };
++joe.age;
joe.wage = 68000.0;
Employee* ptr{ &joe };
std::cout << (*ptr).id << '\n'; // Nie świetne, ale działa: Najpierw wyłuskaj ptr, a następnie użyj wyboru elementu
return 0;
}Jest to jednak trochę brzydkie, szczególnie dlatego, że musimy umieścić operację wyłuskiwania w nawiasach, aby miała ona pierwszeństwo przed operacją wyboru elementu.
Aby zapewnić czystszą składnię, C++ oferuje wybór elementów za pomocą operatora wskaźnika (->) (czasami nazywany także operator strzałki), którego można użyć do wybierania elementów ze wskaźnika do obiektu:
#include <iostream>
struct Employee
{
int id{};
int age{};
double wage{};
};
int main()
{
Employee joe{ 1, 34, 65000.0 };
++joe.age;
joe.wage = 68000.0;
Employee* ptr{ &joe };
std::cout << ptr->id << '\n'; // Lepiej: użyj ->, aby wybrać element ze wskaźnika do obiektu
return 0;
}Ten wybór elementu za pomocą operatora wskaźnika (->) działa identycznie jak operator wyboru elementu (.), ale dokonuje niejawnego wyłuskiwania obiektu wskaźnika przed wybraniem elementu. Zatem ptr->id jest odpowiednikiem (*ptr).id.
Ten operator strzałki jest nie tylko łatwiejszy do wpisania, ale jest także znacznie mniej podatny na błędy, ponieważ pośredniość jest wykonywana niejawnie za Ciebie, więc nie musisz się martwić o kwestie pierwszeństwa. W związku z tym, uzyskując dostęp do elementu członkowskiego za pomocą wskaźnika, zawsze używaj operatora -> zamiast . operator.
Najlepsza praktyka
Jeśli używasz wskaźnika do uzyskania dostępu do elementu, użyj wyboru elementu za pomocą operatora wskaźnika (->) zamiast operatora wyboru elementu (.).
Łączenie operator->
Jeśli element, do którego uzyskujesz dostęp za pośrednictwem operator-> jest wskaźnikiem do typu klasy, operator-> można zastosować ponownie w tym samym wyrażeniu, aby uzyskać dostęp do elementu tej klasy type.
Ilustruje to poniższy przykład (dzięki uprzejmości czytelnika Luny):
#include <iostream>
struct Point
{
double x {};
double y {};
};
struct Triangle
{
Point* a {};
Point* b {};
Point* c {};
};
int main()
{
Point a {1,2};
Point b {3,7};
Point c {10,2};
Triangle tr { &a, &b, &c };
Triangle* ptr {&tr};
// ptr jest wskaźnikiem do trójkąta, który zawiera elementy będące wskaźnikami do punktu
// Aby uzyskać dostęp do elementu y punktu c Trójkąta wskazanego przez ptr, poniższe są równoważne:
// dostęp przez operatora.
std::cout << (*(*ptr).c).y << '\n'; // ugly!
// access via operator->
std::cout << ptr -> c -> y << '\n'; // much nicer
}W przypadku użycia więcej niż jednego operator-> w sekwencji (np. ptr->c->y) wyrażenie może być trudne do odczytania. Dodanie białych znaków między elementami operator-> (np. ptr -> c -> y) może nieco ułatwić odróżnienie elementów, do których uzyskiwany jest dostęp, od operatora.
Mieszanie wskaźników i elementów niebędących wskaźnikami do elementów
Operator wyboru elementu jest zawsze stosowany do aktualnie wybranej zmiennej. Jeśli masz mieszankę wskaźników i zwykłych zmiennych składowych, możesz zobaczyć wybory elementów, w których . i -> są używane kolejno:
#include <iostream>
#include <string>
struct Paw
{
int claws{};
};
struct Animal
{
std::string name{};
Paw paw{};
};
int main()
{
Animal puma{ "Puma", { 5 } };
Animal* ptr{ &puma };
// ptr jest wskaźnikiem, użyj ->
// paw nie jest wskaźnikiem, użyj .
std::cout << (ptr->paw).claws << '\n';
return 0;
}Zauważ, że w przypadku (ptr->paw).claws nawiasy nie są konieczne, ponieważ oba operator-> i operator. oceniają w kolejności od lewej do prawej, ale poprawia to nieco czytelność.

