Specyfikatory dostępu — szczegółowo
Część sekcji Programowanie obiektowe ścieżki C++ w Coddy. Lekcja 35 z 104.
Skoro rozumiesz już trzy specyfikatory dostępu, przyjrzyjmy się, jak działają, gdy masz wiele obiektów tej samej klasy. Powszechnym błędnym przekonaniem jest to, że private oznacza „tylko ten obiekt” — w rzeczywistości oznacza „tylko ta klasa”.
Obiekty tej samej klasy mogą uzyskiwać dostęp do swoich prywatnych składowych:
class Person {
std::string secret;
public:
Person(std::string s) : secret(s) {}
void readSecret(const Person& other) {
// Dostęp do prywatnego pola innego obiektu – dozwolony!
std::cout << other.secret;
}
};Działa to, ponieważ kontrola dostępu jest egzekwowana na poziomie klasy, a nie obiektu. Metoda readSecret jest częścią klasy Person, więc może uzyskiwać dostęp do prywatnych składowych dowolnego obiektu Person. Taka konstrukcja umożliwia działanie operacji takich jak konstruktory kopiujące i operatory porównania, które korzystają ze stanu wewnętrznego innych obiektów.
Możesz też wielokrotnie przełączać się między specyfikatorami dostępu w obrębie klasy:
class Mixed {
public:
void publicMethod1();
private:
int privateData;
public:
void publicMethod2(); // Z powrotem do public
private:
void privateHelper(); // Ponownie private
};Choć jest to poprawny kod C++, zazwyczaj lepiej grupować składowe według poziomu dostępu, aby zwiększyć czytelność. Większość przewodników po stylu zaleca wymienianie najpierw składowych publicznych (ponieważ tworzą interfejs klasy), następnie chronionych, a na końcu prywatnych.
Wyzwanie
ŁatwyZbudujmy system porównywania portfeli, który pokazuje, jak obiekty tej samej klasy mogą uzyskiwać dostęp do swoich prywatnych składowych. To ważna kwestia dotycząca kontroli dostępu w C++: jest ona egzekwowana na poziomie klasy, a nie obiektu.
Utworzysz dwa pliki, aby uporządkować kod:
Wallet.h: Zdefiniuj klasęWallet, która przechowuje prywatne dane finansowe, ale umożliwia porównywanie portfeli. Twoja klasa powinna zawierać:- Prywatne składowe:
ownerName(string) ibalance(double) - Konstruktor, który przyjmuje nazwę właściciela i początkowe saldo
- Publiczny getter
getOwnerName(), który zwraca nazwę właściciela - Metodę
hasMoreThan(const Wallet& other), która porównuje saldo tego portfela z prywatnym saldem innego portfela i zwracatrue, jeśli ten portfel ma więcej pieniędzy - Metodę
combinedBalance(const Wallet& other), która zwraca sumę prywatnych sald obu portfeli - Metodę
transferTo(Wallet& other, double amount), która przenosi pieniądze z tego portfela do innego. Powinna bezpośrednio modyfikować prywatne salda obu portfeli (tylko jeśli kwota jest dodatnia i nie przekracza salda tego portfela)
- Prywatne składowe:
main.cpp: Pokaż dostęp do prywatnych składowych innego obiektu. Wczytaj z wejścia dwie nazwy właścicieli i dwa salda (cztery wiersze: name1, balance1, name2, balance2), a następnie:- Utwórz dwa obiekty
Walletz wczytanymi wartościami - Wypisz
"Combined wealth: $<amount>", używająccombinedBalance() - Porównaj portfele i wypisz
"<name> has more money"dla właściciela z wyższym saldem (użyjhasMoreThan(), aby to ustalić) - Przenieś
100.0z pierwszego portfela do drugiego, używająctransferTo() - Wypisz
"After transfer:" - Ponownie wypisz
"Combined wealth: $<amount>"(wartość powinna pozostać bez zmian) - Ponownie porównaj portfele i wypisz
"<name> has more money"(wynik mógł się zmienić)
- Utwórz dwa obiekty
Kluczowa kwestia jest taka, że metody takie jak hasMoreThan() i transferTo() mogą bezpośrednio uzyskiwać dostęp do prywatnej składowej balance innego obiektu Wallet: nie są potrzebne gettery. Działa to dlatego, że kontrola dostępu w C++ jest oparta na klasach: dowolny kod wewnątrz klasy Wallet może uzyskiwać dostęp do prywatnych składowych dowolnego obiektu Wallet.
Formatuj wszystkie wartości pieniężne z dokładnością do dwóch miejsc po przecinku, używając std::fixed i std::setprecision(2) z <iomanip>. Konwertuj wczytane salda za pomocą std::stod().
Spróbuj swoich sił
#include <iostream>
#include <string>
#include <iomanip>
#include "Wallet.h"
using namespace std;
int main() {
// Wczytaj dane wejściowe: name1, balance1, name2, balance2
string name1, name2;
string balanceStr1, balanceStr2;
getline(cin, name1);
getline(cin, balanceStr1);
getline(cin, name2);
getline(cin, balanceStr2);
double balance1 = stod(balanceStr1);
double balance2 = stod(balanceStr2);
// Ustaw formatowanie wartości pieniężnych na wyjściu
cout << fixed << setprecision(2);
// TODO: Utwórz dwa obiekty Wallet z wartościami wejściowymi
// TODO: Wypisz łączny stan środków, używając combinedBalance()
// Format: "Combined wealth: $<amount>"
// TODO: Porównaj portfele, używając hasMoreThan(), i wypisz, kto ma więcej pieniędzy
// Format: "<name> has more money"
// TODO: Przenieś 100.0 z pierwszego portfela do drugiego, używając transferTo()
// TODO: Wypisz "After transfer:"
// TODO: Ponownie wypisz łączny stan środków
// TODO: Ponownie porównaj portfele i wypisz, kto ma więcej pieniędzy
return 0;
}
Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.
Wszystkie lekcje w sekcji Programowanie obiektowe
1Podstawy programowania obiektowego
Pliki zewnętrzneBudowanie i kompilacja C++Pliki nagłówkowe i pliki źródłowePrzestrzenie nazw i zakresWprowadzenie do programowania obiektowego w C++Klasy a obiektyWskaźnik „this”Metody (funkcje składowe)Atrybuty (składowe danych)Podstawy konstruktorów i destruktorówPowtórzenie – prosty kalkulator4Właściwości klas
Elementy instancji a elementy statyczneGettery i setteryStałe funkcje składoweSłowo kluczowe mutableMetody i zmienne statyczneFunkcje i klasy zaprzyjaźnionePodsumowanie – menedżer konta bankowego7Dziedziczenie
Podstawy dziedziczeniaPoziomy dostępu w dziedziczeniuKolejność wywołań konstruktora i destruktoraNadpisywanie metodFunkcje wirtualne i VTableWielokrotne dziedziczenieDziedziczenie wirtualnePowtórzenie — hierarchia pracowników2Zarządzanie pamięcią
Pamięć stosu a stertyWskaźniki i referencjePamięć dynamiczna (new/delete)Inteligentne wskaźniki w C++RAII w C++Podsumowanie — menedżer tablicy dynamicznej5Hermetyzacja
Specyfikatory dostępu w C++Specyfikatory dostępu — szczegółowoUkrywanie informacjiStruktura a klasaKlasy zagnieżdżone i wewnętrznePodsumowanie — system ewidencji studentów8Polimorfizm
Polimorfizm czasu kompilacji i wykonaniaPrzeciążanie funkcjiFunkcje wirtualne — powtórkaCzysto wirtualne funkcjeKlasy abstrakcyjneProjektowanie interfejsów w C++Rzutowanie dynamiczne i RTTIPodsumowanie — kalkulator kształtów3Konstruktory i destruktory
Konstruktor domyślnyKonstruktor z parametramiKonstruktor kopiującyKonstruktor przenoszącyListy inicjalizacyjne konstruktoraKonstruktory delegująceDestruktor — szczegółowe omówienieZasada trzech / pięciu / zeraPodsumowanie — klasa String6Przeciążanie operatorów
Wprowadzenie do przeciążania operatorówPrzeciążanie operatorów arytmetycznychPrzeciążanie operatorów porównaniaOperatory strumieniowePrzeciążanie operatora przypisaniaPrzeciążanie operatorów [] i ()Operatory konwersji typówPodsumowanie — klasa Matrix9Szablony
Szablony funkcjiSzablony klasSpecjalizacja szablonówSzablony wariadycznePodstawy SFINAE i cech typówPodsumowanie — kontener generycznyPoćwicz samodzielnie: Kompilator C++ online