Zugriffsebenen bei der Vererbung
Teil des Abschnitts Objektorientierte Programmierung der C++-Journey von Coddy. Lektion 49 von 104.
Der von dir gewählte Vererbungstyp wirkt sich darauf aus, wie auf die Member der Basisklasse in der abgeleiteten Klasse zugegriffen wird. C++ bietet drei Optionen: public, protected und private-Vererbung.
So verändert jeder Typ den Zugriff auf Member:
| Basismember | öffentliche Vererbung | geschützte Vererbung | private Vererbung |
|---|---|---|---|
| öffentlich | öffentlich | geschützt | privat |
| geschützt | geschützt | geschützt | privat |
| privat | nicht zugänglich | nicht zugänglich | nicht zugänglich |
class Base {
public:
int pub;
protected:
int prot;
private:
int priv;
};
class PublicDerived : public Base {
// pub ist public, prot ist protected, priv ist unzugänglich
};
class ProtectedDerived : protected Base {
// pub ist protected, prot ist protected, priv ist unzugänglich
};
class PrivateDerived : private Base {
// pub ist private, prot ist private, priv ist unzugänglich
};Öffentliche Vererbung bildet „ist-ein“-Beziehungen ab und ist bei Weitem die häufigste Form. Ein Dog ist ein Animal, daher kann externer Code es als solches behandeln.
Geschützte und private Vererbung modellieren „implemented-in-terms-of“-Beziehungen. Sie verbergen die Schnittstelle der Basisklasse vor externem Code und sind nützlich, wenn du die Implementierung wiederverwenden möchtest, ohne die Vererbungsbeziehung offenzulegen. Bei privater Vererbung verlieren sogar weiter abgeleitete Klassen den Zugriff auf die ursprünglichen Member der Basisklasse.
Aufgabe
EinfachSehen wir uns an, wie sich verschiedene Vererbungstypen auf die Zugänglichkeit von Membern auswirken, indem wir eine einfache Gerätehierarchie erstellen. Du erstellst eine Basisklasse Device und drei abgeleitete Klassen, die jeweils einen anderen Vererbungstyp verwenden, um direkt zu sehen, wie öffentliche, geschützte und private Vererbung den Zugriff auf Member verändert.
Du wirst deinen Code auf zwei Dateien verteilen:
Device.h: Definiere eine BasisklasseDevicemit drei Membern auf unterschiedlichen Zugriffsebenen:- Ein öffentliches
std::string model - Ein geschütztes
int serialNumber - Ein privates
int internalCode(standardmäßig auf 999 gesetzt) - Eine öffentliche Methode
showModel(), die Folgendes ausgibt:Model: <model>
Erstelle anschließend drei abgeleitete Klassen, die jeweils mit einem anderen Vererbungstyp von
Deviceerben:PublicDevicemit öffentlicher Vererbung: Füge eine MethodedisplaySerial()hinzu, die Folgendes ausgibt:Serial: <serialNumber>ProtectedDevicemit geschützter Vererbung: Füge eine MethodedisplaySerial()hinzu, die Folgendes ausgibt:Serial: <serialNumber>PrivateDevicemit privater Vererbung: Füge eine MethodedisplaySerial()hinzu, die Folgendes ausgibt:Serial: <serialNumber>
Jede abgeleitete Klasse sollte einen Konstruktor haben, der einen Modellnamen und eine Seriennummer entgegennimmt, das Modell an die Basisklasse übergibt und die Seriennummer setzt.
- Ein öffentliches
main.cpp: Lies zwei Eingaben ein (jeweils in einer separaten Zeile):- Modellname (String)
- Seriennummer (Ganzzahl)
Erstelle mit denselben Eingabewerten jeweils ein Objekt jedes abgeleiteten Typs. Zeige anschließend die Zugriffsunterschiede:
Für
PublicDevice:- Rufe
showModel()direkt für das Objekt auf (funktioniert, weil die öffentliche Vererbung den Member öffentlich hält) - Greife direkt auf
modelzu und gib Folgendes aus:Direct model access: <model> - Rufe
displaySerial()auf
Für
ProtectedDeviceundPrivateDevice:- Rufe nur
displaySerial()auf (showModel()undmodelsind von außen nicht mehr öffentlich zugänglich)
Ausgabeformat:
--- PublicDevice --- Model: <model> Direct model access: <model> Serial: <serial> --- ProtectedDevice --- Serial: <serial> --- PrivateDevice --- Serial: <serial>
Zum Beispiel mit den Eingaben Laptop und 12345:
--- PublicDevice ---
Model: Laptop
Direct model access: Laptop
Serial: 12345
--- ProtectedDevice ---
Serial: 12345
--- PrivateDevice ---
Serial: 12345Beachte, dass nur PublicDevice den externen Zugriff auf die geerbten öffentlichen Member ermöglicht. Die anderen Vererbungstypen verbergen die Schnittstelle der Basisklasse vor externem Code, aber die abgeleiteten Klassen können weiterhin über ihre eigenen Methoden intern auf geschützte Member zugreifen.
Probier es selbst
#include <iostream>
#include <string>
#include "Device.h"
using namespace std;
int main() {
// Eingabe lesen
string modelName;
int serialNum;
getline(cin, modelName);
cin >> serialNum;
// TODO: Erstelle Objekte jedes abgeleiteten Typs mit den Eingabewerten
// PublicDevice pubDev(modelName, serialNum);
// ProtectedDevice protDev(modelName, serialNum);
// PrivateDevice privDev(modelName, serialNum);
// TODO: Demonstrate PublicDevice access
cout << "--- PublicDevice ---" << endl;
// - Rufe showModel() direkt auf dem Objekt auf
// - Access model directly and print: Direct model access: <model>
// - Rufe displaySerial() auf
// TODO: Demonstrate ProtectedDevice access
cout << "--- ProtectedDevice ---" << endl;
// - Rufe nur displaySerial() auf (showModel und model sind nicht öffentlich zugänglich)
// TODO: Demonstrate PrivateDevice access
cout << "--- PrivateDevice ---" << endl;
// - Rufe nur displaySerial() auf (showModel und model sind nicht öffentlich zugänglich)
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 & QuelldateienNamespaces & GültigkeitsbereichEinführung in OOP mit C++Klassen vs. ObjekteDer „this“-ZeigerMethoden (Member-Funktionen)Attribute (Datenmember)Grundlagen zu Ctors & DtorsRückblick – Einfacher Taschenrechner4Klassenattribute
Instanz- vs. statische MemberGetter und SetterConst-MemberfunktionenMutable-SchlüsselwortStatische Methoden und VariablenFriend-Funktionen und -KlassenZusammenfassung – Bankkontoverwaltung7Vererbung
Grundlagen der VererbungZugriffsebenen bei der VererbungAufrufreihenfolge von Ctor & DtorÜberschreiben von MethodenVirtuelle Funktionen & VTableMehrfachvererbungVirtuelle VererbungRückblick – Mitarbeiterhierarchie2Speicherverwaltung
Stack- vs. Heap-SpeicherZeiger und ReferenzenDynamischer Speicher (new/delete)Intelligente Zeiger in C++RAII in C++Rückblick – Dynamischer Array-Manager5Kapselung
Zugriffsbezeichner in C++Zugriffsbezeichner im DetailInformationsverbergungStruct vs. KlasseVerschachtelte und innere KlassenWiederholung – Studierendenverwaltungssystem8Polymorphie
Polymorphie zur Compilezeit vs. LaufzeitFunktionsüberladungVirtuelle Funktionen erneut betrachtetRein virtuelle FunktionenAbstrakte KlassenInterface-Design in C++Dynamic Casting & RTTIRückblick – Shape-Calculator3Konstruktoren & Destruktoren
StandardkonstruktorParametrisierter KonstruktorKopierkonstruktorMove-KonstruktorKonstruktor-InitialisierungslistenDelegierende KonstruktorenDestruktor im DetailDrei-/Fünf-/Null-RegelZusammenfassung – String-Klasse6Operatorüberladung
Einführung in die OperatorüberladungArithmetische OperatorüberladungVergleichsoperatorüberladungStream-OperatorenZuweisungsoperatorüberladungÜberladung der Operatoren [] und ()TypkonvertierungsoperatorenWiederholung – Matrix-Klasse9Templates
FunktionstemplatesKlassen-TemplatesTemplate-SpezialisierungVariadische TemplatesGrundlagen zu SFINAE und Type TraitsRückblick – Generischer ContainerÜbe selbstständig: Online-C++-Compiler