Menu

Specyfikatory dostępu w C++: public, private, protected

Jak public, private i protected decydują w C++, kto może dotykać składowych klasy: fundament enkapsulacji, z getterami, setterami i furtką w postaci friend.

Na tej stronie są działające edytory: edytuj, uruchamiaj i od razu zobacz wynik.

Kto może dotykać twoich danych

Pierwsze klasy zwykle wystawiają każdą składową na świat zewnętrzny. To działa, ale marnuje jeden z głównych powodów, dla których klasy w ogóle istnieją: enkapsulację, czyli ukrywanie wewnętrznego stanu klasy tak, aby reszta programu mogła się z nią komunikować tylko przez kontrolowany interfejs. Specyfikatory dostępu to sposób na wyznaczenie tej granicy.

Są dokładnie trzy: public, private i protected. Każdy z nich oznacza składowe, które po nim następują, a ta etykieta decyduje, który kod może je odczytywać lub zapisywać. Zrób to dobrze, a twoja klasa sama pilnuje swoich reguł; zrób to źle, a dowolny błąd w dowolnym miejscu może zepsuć stan twojego obiektu.

Trzy specyfikatory

Specyfikator to słowo kluczowe zakończone dwukropkiem. Każda składowa zadeklarowana po nim, aż do następnego specyfikatora, podlega temu poziomowi dostępu.

Odkomentuj ostatnią linię, a kompilator odmówi zbudowania programu: balance jest private, więc main nie może go bezpośrednio ruszać. O to właśnie chodzi: jedyny sposób na zmianę salda to deposit, więc później możesz dodać walidację (bez ujemnych wpłat, logowanie, limity) w jednym miejscu i mieć pewność, że zawsze zadziała.

Oto pełne zestawienie:

//             dostępne z...
// public      dowolnego miejsca (każdego kodu, który ma obiekt)
// private     tylko składowych tej samej klasy (+ przyjaciele)
// protected   składowych tej samej klasy ORAZ klas pochodnych (+ przyjaciele)

class kontra struct: dostęp domyślny

Możesz napisać dowolnie wiele bloków ze specyfikatorami, w dowolnej kolejności. To, jaki dostęp mają składowe przed pierwszym specyfikatorem, zależy od tego, czy użyjesz class, czy struct:

  • W class składowe są domyślnie private.
  • W struct składowe są domyślnie public.

Ta domyślna wartość to jedyna różnica między tymi dwoma słowami kluczowymi na poziomie języka. struct może mieć metody, konstruktory i sekcje private dokładnie tak jak class.

Przyjęło się używać struct do prostych paczek publicznych danych, a class wtedy, gdy potrzebujesz zachowania i ukrytego stanu. Kompilator tego jednak nie wymusza, różni się tylko dostęp domyślny.

Enkapsulacja z getterami i setterami

Codzienny wzorzec wygląda tak: dane trafiają do sekcji private, a metoda public daje do nich kontrolowany dostęp. Getter tylko do odczytu zwraca wartość; setter sprawdza ją przed przypisaniem. Tu właśnie private się opłaca.

Ponieważ celsius jest private, nie da się przemycić niepoprawnej wartości: każdy zapis musi przejść przez setCelsius, który pilnuje niezmiennika. Zwróć uwagę, że gettery są oznaczone jako const: obiecują, że nie zmienią obiektu, więc możesz je wywoływać także na obiektach const Temperature.

protected i dziedziczenie

protected ma znaczenie dopiero wtedy, gdy w grę wchodzi dziedziczenie. Dla kodu z zewnątrz zachowuje się jak private, ale klasa pochodna może po nie sięgać. Używaj go dla składowych, których podklasa naprawdę potrzebuje, ale których świat zewnętrzny nadal nie powinien widzieć.

Typowy błąd początkujących to oznaczanie każdej danej jako protected "na wypadek, gdyby podklasa jej potrzebowała". To po cichu poszerza kontrakt twojej klasy: teraz każda podklasa może zależeć od tego pola i nie zmienisz go już swobodnie. Wybieraj private i awansuj składową do protected tylko wtedy, gdy klasa pochodna naprawdę potrzebuje dostępu.

Furtka friend

Czasem jedna zewnętrzna funkcja lub klasa musi zajrzeć do twoich wnętrzności. Klasyczny przypadek to przeciążanie operatora <<, którego nie możesz zrobić składową. Słowo kluczowe friend daje tej jednej, nazwanej jednostce dostęp do twoich składowych private i protected, i do niczego więcej.

friend to celowy, precyzyjny wyjątek: sama klasa wskazuje dokładnie, komu ufa, więc nikt nie może przyznać sobie dostępu z zewnątrz. Używaj go oszczędnie; jeśli dodajesz wielu przyjaciół, twoje składowe prawdopodobnie w ogóle nie powinny być private albo projekt wymaga przemyślenia.

Częste błędy

  • Robienie każdej składowej public. Wydaje się wygodne, ale tracisz całą walidację i niezmienniki, które daje enkapsulacja. Domyślnie wybieraj dane private i metody public.
  • Zapominanie o domyślnym dostępie w class. class Foo { int x; }; sprawia, że x jest prywatne, więc foo.x = 5 się nie skompiluje. Jeśli chodziło ci o prostą paczkę danych, użyj struct albo dodaj etykietę public:.
  • Nadużywanie protected. To słabsza granica niż private i ma znaczenie tylko przy dziedziczeniu. Stosowanie go wszędzie wiąże podklasy z polami, które możesz chcieć zmienić.
  • Traktowanie private jak zabezpieczenia. To reguła czasu kompilacji, która zapobiega przypadkowemu dostępowi, a nie szyfrowanie. Bajty nadal leżą w pamięci; private służy czystemu projektowi, nie tajności.

Dalej: struktury

Wiesz już, że struct to w gruncie rzeczy class, której składowe są domyślnie public. Następna strona, o strukturach, pokazuje, kiedy ten publiczny dostęp domyślny to dokładnie to, czego chcesz (lekkie agregaty grupujące powiązane wartości), i jak struct współpracuje w idiomatycznym C++ z pełnoprawnymi klasami.

Najczęściej zadawane pytania

Jaka jest różnica między public, private i protected w C++?

Składowe public są dostępne z dowolnego miejsca. Składowe private są dostępne tylko z wnętrza tej samej klasy (i jej przyjaciół, czyli friend). protected działa jak private, ale dodatkowo pozwala klasom pochodnym sięgać po składową. Zgodnie z konwencją dane trzymasz jako private, a zachowanie udostępniasz przez metody public.

Czy składowe są domyślnie prywatne w C++?

W class tak: wszystko jest private, dopóki nie napiszesz specyfikatora dostępu. W struct domyślny dostęp to public. Ta jedna domyślna wartość to jedyna prawdziwa różnica między class a struct w C++; obie mogą mieć metody, konstruktory i specyfikatory dostępu.

Co robi słowo kluczowe friend w C++?

friend daje jednej konkretnej funkcji lub klasie dostęp do twoich składowych private i protected. To celowy, wąski wyjątek od enkapsulacji: klasa wskazuje dokładnie, komu ufa, więc dostęp nigdy nie jest przyznawany niejawnie.

Ilustracja języków programowania w Coddy

Ucz się programowania z Coddy

ZACZNIJ