14.6 — Funkcje dostępu

W poprzedniej lekcji 14.5 -- Członkowie publiczni i prywatni oraz specyfikatory dostępu omawialiśmy poziomy dostępu publicznego i prywatnego. Przypominamy, że klasy zazwyczaj sprawiają, że ich dane są prywatne, a do prywatnych członków nie można bezpośrednio uzyskać dostępu publicznego.

Rozważ następującą Date klasę:

#include <iostream>

class Date
{
private:
    int m_year{ 2020 };
    int m_month{ 10 };
    int m_day{ 14 };

public:
    void print() const
    {
        std::cout << m_year << '/' << m_month << '/' << m_day << '\n';
    }
};

int main()
{
    Date d{};  // create a Date object
    d.print(); // print the date

    return 0;
}

Chociaż ta klasa udostępnia print() funkcję członkowską umożliwiającą wydrukowanie całej daty, może to nie wystarczyć do tego, co użytkownik chce zrobić. Na przykład, co by było, gdyby użytkownik Date obiektu chciał uzyskać rok? Lub zmienić rok na inną wartość? Nie byliby w stanie tego zrobić, ponieważ m_year jest prywatny (i dlatego nie może być bezpośrednio dostępny dla publiczności).

W przypadku niektórych klas może być właściwe (w kontekście tego, co robi klasa), abyśmy mogli uzyskać lub ustawić wartość prywatnej zmiennej członkowskiej.

Funkcje dostępu

An funkcja dostępu jest trywialną wartością publiczną funkcja składowa, której zadaniem jest pobranie lub zmiana wartości prywatnej zmiennej składowej.

Funkcje dostępu występują w dwóch wersjach: pobierające i ustawiające. Gettery (czasami nazywane akcesorami) to publiczne funkcje składowe, które zwracają wartość prywatnego składowej zmienna. Settery (czasami nazywane mutatorami) to publiczne funkcje składowe, które ustawiają wartość prywatnej zmiennej składowej.

Nomenklatura

Termin „mutator” jest często używany zamiennie z terminem „setter”. Ale szerzej, mutator to dowolna funkcja składowa, która modyfikuje (mutuje) stan obiektu. Zgodnie z tą definicją setter jest specyficznym rodzajem mutatora. Jednak możliwe jest również posiadanie funkcji niesetterowych, które kwalifikują się jako mutatory.

Gettery są zwykle wykonane jako stałe, więc można je wywoływać zarówno na obiektach const, jak i innych. Setery nie powinny być stałe, aby mogły modyfikować składowe danych.

Dla celów ilustracyjnych zaktualizujmy naszą Date klasę, aby mieć pełny zestaw getterów i setterów:

#include <iostream>

class Date
{
private:
    int m_year { 2020 };
    int m_month { 10 };
    int m_day { 14 };

public:
    void print()
    {
        std::cout << m_year << '/' << m_month << '/' << m_day << '\n';
    }

    int getYear() const { return m_year; }        // getter for year
    void setYear(int year) { m_year = year; }     // setter for year

    int getMonth() const  { return m_month; }     // getter for month
    void setMonth(int month) { m_month = month; } // setter for month

    int getDay() const { return m_day; }          // getter for day
    void setDay(int day) { m_day = day; }         // setter for day
};

int main()
{
    Date d{};
    d.setYear(2021);
    std::cout << "The year is: " << d.getYear() << '\n';

    return 0;
}

Wypisuje:

The year is: 2021

Nazewnictwo funkcji dostępu

Nie ma wspólnej konwencji nazewnictwa funkcji dostępu. Istnieje jednak kilka konwencji nazewnictwa, które są bardziej popularne niż inne.

  • Przedrostek „get” i „set”:
    int getDay() const { return m_day; }  // getter
    void setDay(int day) { m_day = day; } // setter

Zaletą używania przedrostków „get” i „set” jest to, że jest jasne, że są to funkcje dostępu (i ich wywołanie powinno być niedrogie).

  • Bez przedrostka:
    int day() const { return m_day; }  // getter
    void day(int day) { m_day = day; } // setter

Ten styl jest bardziej zwięzły i używa tej samej nazwy dla obu moduł pobierający i ustawiający (polegający na przeciążeniu funkcji w celu rozróżnienia tych dwóch). Standardowa biblioteka C++ wykorzystuje tę konwencję.

Wadą konwencji bez prefiksu jest to, że nie jest szczególnie oczywiste, że jest to ustawienie wartości elementu day:

d.day(5); // does this look like it's setting the day member to 5?

Kluczowa informacja

Jednym z najlepszych powodów poprzedzania prywatnych elementów danych „m_” jest unikanie elementów danych i modułów pobierających o tej samej nazwie (coś, czego C++ nie obsługuje, chociaż robią to inne języki, takie jak Java).

  • Przedrostek „set”. tylko:
    int day() const { return m_day; }     // getter
    void setDay(int day) { m_day = day; } // setter

Który z powyższych wybierzesz, jest kwestią osobistych preferencji. Jednakże zdecydowanie zalecamy używanie przedrostka „set” w przypadku osób ustawiających. Gettery mogą używać przedrostka „get” lub nie mieć przedrostka.

Wskazówka

Użyj przedrostka „set” w swoich seterach, aby było bardziej oczywiste, że zmieniają stan obiektu.

Gettery powinny zwracać przez wartość lub przez odwołanie do stałej lwartości

Gettery powinny zapewniać dostęp do danych w trybie „tylko do odczytu”. Dlatego najlepszą praktyką jest to, że powinny zwracać albo wartość (jeśli utworzenie kopii elementu jest niedrogie), albo odwołanie do stałej lwartości (jeśli wykonanie kopii elementu jest kosztowne).

Ponieważ zwracanie elementów danych przez odwołanie nie jest trywialnym tematem, omówimy ten temat bardziej szczegółowo w lekcji 14.7 — Funkcje składowe zwracające odniesienia do elementów danych.

Funkcje dostępu obawy

Toczy się spora dyskusja na temat przypadków, w których należy używać funkcji dostępu lub których należy unikać. Wielu programistów twierdziłoby, że używanie funkcji dostępu narusza dobry projekt klas (temat, który z łatwością mógłby wypełnić całą książkę).

Na razie zalecamy podejście pragmatyczne. Tworząc klasy, weź pod uwagę następujące kwestie:

  • Jeśli Twoja klasa nie ma niezmienników i wymaga wielu funkcji dostępu, rozważ użycie struktury (której składowe danych są publiczne) i zamiast tego zapewnienie członkom bezpośredniego dostępu.
  • Wolę wdrażanie zachowań lub akcji zamiast funkcji dostępu. Na przykład zamiast setAlive(bool) settera zaimplementuj a kill() oraz a revive() .
  • Udostępniaj funkcje dostępu tylko w przypadkach, gdy społeczeństwo w uzasadniony sposób musiałoby uzyskać lub ustawić wartość pojedynczego elementu członkowskiego.

Po co ustawiać dane jako prywatne, skoro mamy zapewnić do nich funkcję publicznego dostępu?

Cieszymy się, że zapytaliście. Odpowiemy na to pytanie w nadchodzącej lekcji 14.8 — Korzyści z ukrywania danych (hermetyzowania).

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:  
231 Komentarze
Najnowsze
Najstarsze Najczęściej głosowane
Wbudowane opinie
Wyświetl wszystkie komentarze