13.12 — Wybór elementów za pomocą wskaźników i odwołań

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ść.

guest
Twój adres e-mail nie zostanie wyświetlony
Znalazłeś błąd? Zostaw komentarz powyżej!
Komentarze związane z poprawkami zostaną usunięte po przetworzeniu, aby pomóc zmniejszyć bałagan. Dziękujemy za pomoc w ulepszaniu witryny dla wszystkich!
Awatary z https://gravatar.com/ są połączone z podanym adresem e-mail.
Powiadamiaj mnie o odpowiedziach:  
105 Komentarze
Najnowsze
Najstarsze Najczęściej głosowane
Wbudowane opinie
Wyświetl wszystkie komentarze