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; } // setterZaletą 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; } // setterTen 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; } // setterKtó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 akill()oraz arevive(). - 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).

