Zugriffsspezifizierer im Detail
Teil des Abschnitts Objektorientierte Programmierung der C++-Journey von Coddy — Lektion 35 von 104.
Nachdem Sie nun die drei Zugriffsspezifizierer verstehen, lassen Sie uns untersuchen, wie sie sich verhalten, wenn Sie mehrere Objekte derselben Klasse haben. Ein häufiges Missverständnis ist, dass private „nur dieses Objekt“ bedeutet – aber eigentlich bedeutet es „nur diese Klasse“.
Objekte derselben Klasse können auf die privaten Member der jeweils anderen zugreifen:
class Person {
std::string secret;
public:
Person(std::string s) : secret(s) {}
void readSecret(const Person& other) {
// Zugriff auf das private Member eines anderen Objekts - erlaubt!
std::cout << other.secret;
}
};Dies funktioniert, weil die Zugriffskontrolle auf der Klassenebene erzwungen wird, nicht auf der Objektebene. Die Methode readSecret ist Teil der Klasse Person, sodass sie auf private Member jedes Person-Objekts zugreifen kann. Dieses Design ermöglicht es Operationen wie Kopierkonstruktoren und Vergleichsoperatoren, mit dem internen Zustand anderer Objekte zu arbeiten.
Sie können innerhalb einer Klasse auch mehrfach zwischen Zugriffsspezifizierern wechseln:
class Mixed {
public:
void publicMethod1();
private:
int privateData;
public:
void publicMethod2(); // Zurück zu public
private:
void privateHelper(); // Wieder private
};Obwohl dies gültiges C++ ist, ist es im Allgemeinen besser, Member für eine bessere Lesbarkeit nach Zugriffsebene zu gruppieren. Die meisten Styleguides empfehlen, public Member zuerst aufzulisten (da sie die Klassenschnittstelle bilden), gefolgt von protected und dann private.
Aufgabe
EinfachLassen Sie uns ein Wallet-Vergleichssystem erstellen, das demonstriert, wie Objekte derselben Klasse auf die privaten Member der jeweils anderen zugreifen können. Dies ist eine wichtige Erkenntnis über die Zugriffskontrolle in C++ — sie wird auf Klassenebene erzwungen, nicht auf Objektebene.
Sie werden zwei Dateien erstellen, um Ihren Code zu organisieren:
Wallet.h: Definieren Sie eineWallet-Klasse, die private Finanzdaten speichert, aber Wallet-zu-Wallet-Vergleiche ermöglicht. Ihre Klasse sollte Folgendes enthalten:- Private Member:
ownerName(string) undbalance(double) - Einen Konstruktor, der den Namen des Besitzers und das Anfangsguthaben entgegennimmt
- Einen öffentlichen Getter
getOwnerName(), der den Namen des Besitzers zurückgibt - Eine Methode
hasMoreThan(const Wallet& other), die das Guthaben dieses Wallets mit dem privaten Guthaben eines anderen Wallets vergleicht undtruezurückgibt, wenn dieses Wallet mehr Geld hat - Eine Methode
combinedBalance(const Wallet& other), die die Summe der privaten Guthaben beider Wallets zurückgibt - Eine Methode
transferTo(Wallet& other, double amount), die Geld von diesem Wallet auf ein anderes überträgt — sie sollte die privaten Guthaben beider Wallets direkt ändern (nur wenn der Betrag positiv ist und das Guthaben dieses Wallets nicht übersteigt)
- Private Member:
main.cpp: Demonstrieren Sie den objektübergreifenden Zugriff auf private Member. Lesen Sie zwei Besitzernamen und zwei Guthaben von der Eingabe ein (vier Zeilen: name1, balance1, name2, balance2) und führen Sie dann Folgendes aus:- Erstellen Sie zwei
Wallet-Objekte mit den Eingabewerten - Geben Sie
"Combined wealth: "Combined wealth: $unter Verwendung von"lt;amount>" combinedBalance()aus - Vergleichen Sie die Wallets und geben Sie
"<name> has more money"für den Besitzer aus, der das höhere Guthaben hat (verwenden SiehasMoreThan(), um dies zu bestimmen) - Übertragen Sie
100.0vom ersten Wallet auf das zweite mittransferTo() - Geben Sie
"After transfer:"aus - Geben Sie erneut
"Combined wealth: "Combined wealth: $aus (sollte unverändert sein)"lt;amount>" - Vergleichen Sie erneut und geben Sie
"<name> has more money"aus (das Ergebnis kann sich geändert haben)
- Erstellen Sie zwei
Das Schlüsselkonzept hierbei ist, dass Methoden wie hasMoreThan() und transferTo() direkt auf den privaten Member balance eines anderen Wallet-Objekts zugreifen können — es sind keine Getter erforderlich. Dies funktioniert, weil die Zugriffskontrolle in C++ klassenbasiert ist: Jeder Code innerhalb der Klasse Wallet kann auf private Member jedes Wallet-Objekts zugreifen.
Formatieren Sie alle Geldwerte mit zwei Dezimalstellen unter Verwendung von std::fixed und std::setprecision(2) aus <iomanip>. Konvertieren Sie die Guthaben-Eingaben mit std::stod().
Spickzettel
Die Zugriffskontrolle in C++ wird auf Klassenebene erzwungen, nicht auf Objektebene. Das bedeutet, dass Objekte derselben Klasse auf die privaten Member der jeweils anderen zugreifen können:
class Person {
std::string secret;
public:
Person(std::string s) : secret(s) {}
void readSecret(const Person& other) {
// Zugriff auf das private Member eines anderen Objekts - erlaubt!
std::cout << other.secret;
}
};Die Methode readSecret kann auf das private Member secret jedes Person-Objekts zugreifen, da sie Teil der Klasse Person ist. Dieses Design ermöglicht Operationen wie Kopierkonstruktoren und Vergleichsoperatoren.
Sie können innerhalb einer Klasse mehrmals zwischen Zugriffsspezifizierern wechseln:
class Mixed {
public:
void publicMethod1();
private:
int privateData;
public:
void publicMethod2(); // Zurück zu public
private:
void privateHelper(); // Wieder private
};Es ist jedoch eine bessere Praxis, Member zur besseren Lesbarkeit nach Zugriffsebene zu gruppieren. Die meisten Styleguides empfehlen, zuerst öffentliche Member aufzuführen, gefolgt von geschützten und dann privaten.
Probier es selbst
#include <iostream>
#include <string>
#include <iomanip>
#include "Wallet.h"
using namespace std;
int main() {
// Eingabe lesen: 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);
// Ausgabeformatierung für Währungswerte festlegen
cout << fixed << setprecision(2);
// TODO: Zwei Wallet-Objekte mit den Eingabewerten erstellen
// TODO: Gesamtes Vermögen mit combinedBalance() ausgeben
// Format: "Combined wealth: $<amount>"
// TODO: Wallets mit hasMoreThan() vergleichen und ausgeben, wer mehr Geld hat
// Format: "<name> has more money"
// TODO: 100.0 von der ersten Wallet auf die zweite mit transferTo() übertragen
// TODO: "After transfer:" ausgeben
// TODO: Gesamtes Vermögen erneut ausgeben
// TODO: Erneut vergleichen und ausgeben, wer mehr Geld hat
return 0;
}
Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.
Alle Lektionen in Objektorientierte Programmierung
1Grundlagen der OOP
Externe DateienC++ Build & KompilierungHeader-Dateien & QuelldateienNamensräume & GültigkeitsbereicheEinführung in OOP in C++Klassen vs. ObjekteDer 'this'-PointerMethoden (Elementfunktionen)Attribute (Datenelemente)Grundlagen zu Ctors & DtorsRückblick - Einfacher Taschenrechner4Klasseneigenschaften
Instanz- vs. statische MemberGetter und SetterConst-MemberfunktionenMutable-SchlüsselwortStatische Methoden und VariablenFriend-Funktionen & KlassenZusammenfassung - Bankkonto-Manager7Vererbung
Grundlagen der VererbungZugriffsstufen bei VererbungAufrufreihenfolge von Ctor & DtorÜberschreiben von MethodenVirtuelle Funktionen & VTableMehrfachvererbungVirtuelle VererbungRückblick - Mitarbeiter-Hierarchie2Speicherverwaltung
Stack vs. Heap SpeicherZeiger und ReferenzenDynamischer Speicher (new/delete)Smart Pointers in C++RAII in C++Rückblick – Dynamischer Array-Manager5Kapselung
Zugriffsspezifizierer in C++Zugriffsspezifizierer im DetailInformation HidingStruct vs. ClassVerschachtelte & innere KlassenRückblick - Studentenverwaltungssystem8Polymorphie
Kompilierzeit- vs. Laufzeit-PolymorphieFunktionsüberladungVirtuelle Funktionen – WiederholungRein virtuelle FunktionenAbstrakte KlassenInterface-Design in C++Dynamic Casting & RTTIZusammenfassung – Formen-Rechner3Konstruktoren & Destruktoren
StandardkonstruktorParametrisierter KonstruktorKopierkonstruktorVerschiebekonstruktorKonstruktor-InitialisierungslistenDelegierende KonstruktorenDestruktoren im DetailRule of Three / Five / ZeroZusammenfassung - String-Klasse6Operatorüberladung
Einführung in die OperatorüberladungArithmetische OperatorüberladungVergleichsoperatorüberladungStream-OperatorenZuweisungsoperatorüberladungÜberladen der [] und () OperatorenTypumwandlungsoperatorenRückblick - Matrix-Klasse9Templates
FunktionstemplatesKlassentemplatesTemplate-SpezialisierungVariadische TemplatesSFINAE & Type Traits GrundlagenRückblick - Generische Container