Programmieren in C++: Einführung


Kapitel 6: Strukturen


Inhaltsverzeichnis

Dieses Buch ist unter einer Creative Commons-Lizenz lizensiert.


6.1 Allgemeines

Beschreibung

In Ihren Beispielprogrammen haben Sie bereits eine Menge Datentypen verwendet: int zum Speichern von Zahlen, char zum Speichern von Zeichen, std::string zum Speichern von Wörtern oder Sätzen und so weiter. Was aber, wenn Sie beispielsweise eine Adressverwaltung entwickeln möchten und gerne einen Datentyp verwenden möchten, mit dem Sie Adressen speichern können? Genau für solche Fälle bieten sich Strukturen an.


6.2 Definition

Einen neuen Datentyp erstellen

Bisher konnten Sie lediglich Variablen von Datentypen erstellen, die in C++ von Haus aus bekannt sind - die intrinsischen Datentypen - oder die Sie über Header-Dateien mit #include eingebunden und damit bekanntgemacht haben. Eine Struktur ist nun ein benutzerdefinierter Datentyp - ein Datentyp, den Sie selbst erstellen. Dieser Datentyp besteht genaugenommen aus Variablen anderer Datentypen.

Sehen Sie sich folgendes Beispiel an, in dem ein Datentyp zur Speicherung einer Adresse erstellt wird.

#include <string> 

struct adresse 
{ 
  std::string Anrede; 
  std::string Vorname; 
  std::string Nachname; 
  std::string Strasse; 
  int Hausnummer; 
  int Postleitzahl; 
  std::string Ort; 
  std::string Land; 
}; 

Um einen neuen Datentyp zu erstellen, geben Sie zuerst das Schlüsselwort struct an. Hinter struct folgt der Name des neuen Datentyps. Im obigen Beispiel heißt der neue Datentyp also adresse. Hinter dem Namen werden in geschweiften Klammern nun eine Reihe von Variablen definiert, aus denen der neue Datentyp bestehen soll. Im obigen Code-Beispiel soll also eine Adresse Daten zu Anrede, Vorname, Nachname, Strasse, Hausnummer, Postleitzahl, Ort und Land speichern können. Demnach sind acht Variablen innerhalb der Struktur definiert, die alle den jeweils optimalen Datentyp besitzen, um die gewünschten Daten speichern zu können.

Definitionen neuer Datentypen müssen immer außerhalb von Funktionen erfolgen. Das Schlüsselwort struct darf daher nie innerhalb einer Funktion stehen. Beachten Sie außerdem, dass hinter der geschlossenen geschweiften Klammer einer Struktur-Definition ein Semikolon stehen muss. Andernfalls meldet der Compiler einen Fehler.


6.3 Anwendung

Variable mit neuem Datentyp anlegen

Eine Struktur muss genauso wie eine Funktion zuerst definiert werden, bevor sie angewandt werden kann. Die Definition haben Sie bereits kennengelernt, sie erfolgt über das Schlüsselwort struct. Ist eine Struktur definiert und damit ein neuer Datentyp erstellt worden, können Sie Variablen von diesem neuen Datentyp anlegen. Betrachten Sie hierzu folgendes Beispiel.

#include <string> 

struct adresse 
{ 
  std::string Anrede; 
  std::string Vorname; 
  std::string Nachname; 
  std::string Strasse; 
  int Hausnummer; 
  int Postleitzahl; 
  std::string Ort; 
  std::string Land; 
}; 

int main() 
{ 
  adresse MeineAdresse; 
} 

Innerhalb der Funktion main() wird eine Variable namens MeineAdresse angelegt. Diese Variable erhält als Datentyp die darüber definierte Struktur, nämlich adresse. In dieser Variablen können also nun Daten vom Typ adresse abgelegt werden. Sie können in dieser Variablen beispielsweise Ihre Anschrift speichern.

#include <string> 

struct adresse 
{ 
  std::string Anrede; 
  std::string Vorname; 
  std::string Nachname; 
  std::string Strasse; 
  int Hausnummer; 
  int Postleitzahl; 
  std::string Ort; 
  std::string Land; 
}; 

int main() 
{ 
  adresse MeineAdresse; 

  MeineAdresse.Anrede = "Herr"; 
  MeineAdresse.Vorname = "Boris"; 
  MeineAdresse.Nachname = "Schaeling"; 
  MeineAdresse.Strasse = "Schlossallee"; 
  MeineAdresse.Hausnummer = 1; 
  MeineAdresse.Postleitzahl = 12345; 
  MeineAdresse.Ort = "Entenhausen"; 
  MeineAdresse.Land = "Deutschland"; 
} 

Der Zugriff auf Strukturen erfolgt genaugenommen nie auf die gesamte Struktur selber, sondern auf die einzelnen Bestandteile. Nachdem in der Struktur adresse Variablen wie Anrede, Vorname und Nachname definiert sind, greifen Sie im Code auch auf genau diese Bestandteile zu. Der genaue Zugriff sieht so aus, dass zuerst der Variablenname und dann der Zugriffsoperator angegeben wird. Der Zugriffsoperator ist ., also einfach ein Punkt. Hinter dem Zugriffsoperator wird dann der Name der Variablen angegeben, auf die innerhalb der Struktur zugegriffen werden soll.

Eine Struktur stellt demnach lediglich eine Gruppierung verschiedener Variablen dar. Im Code selber wird mit den einzelnen Bestandteilen der Struktur gearbeitet. Für den Zugriff muss zwar jedesmal der Name der Strukturvariablen und der Zugriffsoperator angegeben werden. Trotzdem verhalten sich die einzelnen in der Struktur definierten Variablen so wie gewohnt. Sie können demnach mit diesen über den Zugriffsoperator angesprochenen Variablen genauso arbeiten wie Sie es bisher von anderen Variablen her kennen.

Es besteht die Möglichkeit, Strukturen zu verschachteln und eine Strukturvariable als Bestandteil einer anderen Struktur zu definieren. Betrachten Sie folgendes Beispiel.

#include <string> 

struct adresse 
{ 
  std::string Anrede; 
  std::string Vorname; 
  std::string Nachname; 
  std::string Strasse; 
  int Hausnummer; 
  int Postleitzahl; 
  std::string Ort; 
  std::string Land; 
}; 

struct bestellung 
{ 
  int Kundennummer; 
  adresse Adresse; 
  std::string Ware; 
}; 

Die Struktur bestellung enthält eine Variable Adresse, die vom Typ adresse ist - also vom Typ einer anderen definierten Struktur. Auf diese Weise lassen sich Daten sehr gut strukturieren, was ja genau Sinn und Zweck von Strukturen ist.

Der Zugriff auf beispielsweise den Nachnamen einer Adresse innerhalb einer Bestellung sieht wie folgt aus.

bestellung MeineBestellung; 

MeineBestellung.Adresse.Nachname = "Schaeling"; 

Über den ersten Zugriffsoperator wird auf die Variable Adresse zugegriffen, die Bestandteil des Datentyps bestellung ist. Nachdem Adresse ihrerseits eine Struktur ist, wird über einen zweiten Zugriffsoperator auf den Nachnamen zugegegriffen, der als Bestandteil innerhalb der Struktur adresse definiert ist.

Beachten Sie, dass die Verwendung von benutzerdefinierten Datentypen wie Strukturen voraussetzt, dass vor dem Anlegen der Variable in einer Datei der Datentyp mit struct definiert ist. Die Definition des Datentyps muss also oberhalb der Variablendefinition erfolgen. Andernfalls meckert der Compiler.


6.4 Enumerationen

Datentyp mit benutzerdefinierten Werten

Auch wenn Enumerationen keine Strukturen sind, so sollen sie dennoch in diesem Kapitel näher vorgestellt werden. Sie ermöglichen nämlich ähnlich wie Strukturen eine bessere problem- und lösungsorientierte Programmierung.

Während mit dem Schlüsselwort struct Datentypen erstellt werden können, die Variablen zu einer Gruppe zusammenfassen, werden mit dem Schlüsselwort enum ganz bestimmte Werte für einen neuen Datentyp definiert. Sehen Sie sich dazu folgendes Beispiel an.

enum land 
{ 
  Deutschland, Oesterreich, Schweiz 
}; 

Mit dem Schlüsselwort enum beginnt die Definition einer Enumeration. Hinter enum folgt der Name des neuen Datentyps - im obigen Beispiel lautet er land. Danach folgen wie bei der Struktur geschweifte Klammern, zwischen denen die eigentliche Definition des Datentypen steht. Beachten Sie, dass auch bei einer Enumeration hinter der geschlossenen geschweiften Klammer ein Semikolon angegeben werden muss.

Was muss nun zwischen den geschweiften Klammern stehen? Eine Enumeration ist ein Datentyp, der auf ganz bestimmte Werte gesetzt werden kann. Die Werte, die für den neuen Datentyp gültig sind, werden einfach zwischen geschweiften Klammern angegeben und jeweils per Komma getrennt. Für den Datentyp land sind demnach die Werte Deutschland, Oesterreich und Schweiz gültig.

Nachdem der Datentyp definiert ist, könnte er wie folgt in einem Programm verwendet werden.

enum land 
{ 
  Deutschland, Oesterreich, Schweiz 
}; 

int main() 
{ 
  land Land = Deutschland; 
} 

Innerhalb der Funktion main() wird eine Variable Land definiert, die vom Datentyp land ist. Diese Variable wird mit dem Wert Deutschland initialisiert. Sehen Sie genau hin: Der Wert Deutschland steht nicht in Anführungszeichen. Es handelt sich hierbei nicht um eine Zeichenkette, die der Variablen zugewiesen wird, sondern tatsächlich nur um den Wert Deutschland. Das ist deswegen möglich, weil der Datentyp land ausdrücklich den Wert Deutschland akzeptiert.

Intern besteht eine Enumeration aus dem Datentyp int. Im obigen Beispiel ist also Land technisch betrachtet eine int-Variable. Den in einer Enumeration definierten Werten wird einfach intern jeweils eine Zahl zugeordnet. Der erste Wert erhält hierbei die Zahl 0, der zweite die Zahl 1, der dritte die Zahl 2 und so weiter. Wenn Sie möchten, dass die interne Darstellung der Werte nicht bei 0, sondern bei 100 beginnt, so können Sie dies direkt bei der Definition der Enumeration angeben.

enum land 
{ 
  Deutschland = 100, Oesterreich, Schweiz 
}; 

Der erste Wert wird intern als Zahl 100 gehandhabt. Dem nächsten Wert Oesterreich wird die nächsthöhere Zahl zugewiesen - in diesem Fall also 101. Möchten Sie, dass Oesterreich eine andere Zahl erhält, müssen Sie diese wieder explizit angeben.

enum land 
{ 
  Deutschland = 100, Oesterreich = 150, Schweiz 
}; 

Die Tatsache, dass eine Enumeration intern lediglich ein int ist, bedeutet, dass einer Variablen vom Typ einer Enumeration auch jede beliebige Zahl zugewiesen werden kann.

enum land 
{ 
  Deutschland, Oesterreich, Schweiz 
}; 

int main() 
{ 
  land Land = static_cast<land>(0); 
} 

Ob Sie der Variablen Land im obigen Beispiel die Zahl 0 zuweisen oder den Wert Deutschland spielt keine Rolle - das Ergebnis ist das gleiche. Sie müssen jedoch die Zahl 0 explizit mit static_cast in einen Wert vom Typ land umwandeln, da land ein anderer Datentyp ist als int.

Sie können Land jede beliebige Ganzzahl zuweisen - selbst dann, wenn die zugewiesene Zahl keinem Wert der Enumeration entspricht. Daher kompiliert auch folgendes Programm fehlerfrei.

enum land 
{ 
  Deutschland, Oesterreich, Schweiz 
}; 

int main() 
{ 
  land Land = static_cast<land>(100); 
} 

Variablen vom Typ einer Enumeration können nicht nur als lokale oder globale Variablen definiert werden, sondern selbstverständlich auch innerhalb einer Struktur Verwendung finden.


6.5 Aufgaben

Übung macht den Meister

Sie können die Lösungen zu allen Aufgaben in diesem Buch als ZIP-Datei erwerben.

  1. Entwickeln Sie eine C++-Anwendung, die den Anwender zur Eingabe einer Bestellung auffordert. Der Anwender muss hierbei der Reihe nach die bestellte Ware, die Lieferanschrift, die Kreditkartennummer und das Gültigkeitsdatum der Kreditkarte eingeben. Die Daten sollen hierbei in geeigneten Variablen unter Zuhilfenahme benutzerdefinierter Strukturen und Enumerationen gespeichert werden. Lieferanschrift und Kreditkartendaten sollen jeweils in einer eigenen Struktur gespeichert werden. Die Bestellung wird ebenfalls in einer eigenen Struktur gespeichert, die sich der anderen beiden Strukturen bedient. Außerdem soll das Land, das in der Lieferanschrift gespeichert wird, vom Typ einer Enumeration sein.

  2. Erweitern Sie die C++-Anwendung aus Aufgabe 1 dahingehend, dass der Anwender bis zu maximal zehn Bestellungen vornehmen kann, die jedoch alle an die gleiche Lieferanschrift gesendet und alle über die gleiche Kreditkarte abgerechnet werden.

  3. Erweitern Sie die C++-Anwendung aus Aufgabe 2 dahingehend, dass Sie eine Funktion definieren, die keinen Rückgabewert liefert und einen einzigen Parameter erwartet. Diese Funktion rufen Sie auf, nachdem der Anwender seine Bestellung vorgenommen hat, und übergeben ihr die gesamte Bestellung. Die Funktion soll daraufhin die bestellten Waren, die Lieferadresse und die Kreditkartendaten auf den Bildschirm ausgeben.