Verschachtelte und innere Klassen
Teil des Abschnitts Objektorientierte Programmierung der C++-Journey von Coddy. Lektion 38 von 104.
Eine verschachtelte Klasse ist eine Klasse, die innerhalb einer anderen Klasse definiert ist. Diese Technik hilft dabei, Code zu organisieren, indem verwandte Typen zusammengefasst werden, und kann die Kapselung verbessern, indem Implementierungsdetails verborgen werden.
class LinkedList {
public:
class Node { // Verschachtelte Klasse
int data;
Node* next;
public:
Node(int d) : data(d), next(nullptr) {}
friend class LinkedList;
};
private:
Node* head;
public:
LinkedList() : head(nullptr) {}
};Die verschachtelte Klasse Node ist eng an LinkedList gekoppelt – sie ergibt nur in diesem Kontext Sinn. Indem wir sie verschachteln, signalisieren wir diese Beziehung und vermeiden, den globalen Namensraum mit einem generischen Namen wie "Node" zu verunreinigen.
Verschachtelte Klassen folgen denselben Zugriffsregeln, die du kennengelernt hast. Eine verschachtelte Klasse, die im privaten Abschnitt deklariert ist, ist nur innerhalb der äußeren Klasse zugänglich.
Wenn es als public deklariert ist, kann externer Code es mit dem Gültigkeitsbereichsoperator verwenden:
LinkedList::Node node(42); // Nur wenn Node public istEin wichtiges Detail zur Zugriffsrichtung: Eine verschachtelte Klasse ist ein Member der einschließenden Klasse und hat dieselben Zugriffsrechte wie jedes andere Member. Daher kann sie ohne eine Friend-Deklaration auf die privaten Member der einschließenden Klasse zugreifen. Umgekehrt gilt dies jedoch nicht automatisch: Die einschließende Klasse hat keinen Zugriff auf die privaten Member der verschachtelten Klasse.
Deshalb deklariert Node im obigen Beispiel friend class LinkedList;: Die friend-Deklaration gewährt der einschließenden LinkedList Zugriff auf die privaten Member von Node – die eine Richtung, die nicht automatisch erfolgt.
Aufgabe
EinfachErstellen wir ein einfaches Aufgabenverwaltungssystem, in dem Aufgaben innerhalb eines Projekts organisiert werden. Dies ist ein perfektes Szenario für verschachtelte Klassen: Eine Task ergibt nur im Kontext eines Project Sinn, daher verschachteln wir sie darin, um unseren Code übersichtlich zu halten und den globalen Namensraum nicht zu verunreinigen.
Du wirst zwei Dateien erstellen, um deinen Code zu organisieren:
Project.h: Definiere eineProject-Klasse, die eine verschachtelteTask-Klasse enthält.Deine verschachtelte
Task-Klasse sollte im public-Bereich vonProjectdeklariert werden, damit sie von außerhalb zugänglich ist. Sie sollte einen Aufgabennamen (String) und einen Abschlussstatus (bool) speichern. Gib ihr einen Konstruktor, der den Namen entgegennimmt und die Aufgabe standardmäßig als nicht abgeschlossen festlegt. Füge die Methodencomplete()zum Markieren als erledigt,isComplete()zum Überprüfen ihres Status undgetName()zum Abrufen des Namens hinzu.Die
Project-Klasse selbst sollte einen Projektnamen (String) und eine Sammlung vonTask-Zeigern speichern. Stelle einen Konstruktor bereit, der den Projektnamen entgegennimmt, eine MethodeaddTask(const std::string& taskName), die eine neue Aufgabe erstellt und sie dem Projekt hinzufügt, eine MethodecompleteTask(const std::string& taskName), die eine Aufgabe anhand ihres Namens findet und abschließt, sowie eine MethodedisplayStatus(), die den Projektnamen gefolgt vom Status jeder Aufgabe anzeigt.Da
Taskinnerhalb vonProjectverschachtelt ist, kann dieProject-Klasse direkt mit Aufgaben arbeiten. Vergiss nicht, die dynamisch angelegten Aufgaben im Destruktor freizugeben.main.cpp: Führe alles zusammen, indem du einen Projektnamen und zwei Aufgabennamen aus der Eingabe einliest (insgesamt drei Zeilen). Erstelle einProjectmit dem angegebenen Namen, füge beide Aufgaben hinzu und zeige anschließend den anfänglichen Status an. Schließe die erste Aufgabe ab und zeige den Status erneut an, um die Änderung sichtbar zu machen.Das Ausgabeformat für
displayStatus()sollte folgendermaßen aussehen:Project: <project_name> - <task_name>: [incomplete] - <task_name>: [complete]Verwende
[complete]oder[incomplete]abhängig vom Status der jeweiligen Aufgabe.
Diese Aufgabe zeigt, wie verschachtelte Klassen dabei helfen, verwandte Typen zu organisieren: Die Task-Klasse ist eng an Project gekoppelt und würde für sich allein keinen Sinn ergeben. Durch die Verschachtelung machen wir diese Beziehung in unserer Codestruktur deutlich.
Probier es selbst
#include <iostream>
#include <string>
#include "Project.h"
using namespace std;
int main() {
// Eingabe lesen
string projectName;
string task1Name;
string task2Name;
getline(cin, projectName);
getline(cin, task1Name);
getline(cin, task2Name);
// TODO: Erstelle ein Project mit dem gegebenen Namen
// TODO: Füge beide Aufgaben zum Project hinzu
// TODO: Zeige den anfänglichen Status an
// TODO: Schließe die erste Aufgabe ab
// TODO: Zeige den Status erneut an, um die Änderung zu zeigen
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