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 };
// Use member selection operator (.) to select a member from struct object
++joe.age; // Joe had a birthday
joe.wage = 68000.0; // Joe got a promotion
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)
{
// Use member selection operator (.) to select member from reference to struct
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'; // Compile error: can't use operator. with pointers
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'; // Not great but works: First dereference ptr, then use member selection
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'; // Better: use -> to select member from pointer to object
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 is a pointer to a Triangle, which contains members that are pointers to a Point
// To access member y of Point c of the Triangle pointed to by ptr, the following are equivalent:
// access via operator.
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 is a pointer, use ->
// paw is not a pointer, use .
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ść.

