|
Home - Programmieren - C++ - c++
Hinweis: Für den hier dargestellte Inhalt ist nicht der Betreiber der Plattform, sondern der jeweilige Autor verantwortlich. Falls Sie Missbrauch vermuten, bitten wir Sie, uns unter missbrauch@it-academy.cc zu kontaktieren. [Druckansicht] [Als E-Mail senden] [Kommentar verfassen] Klassen in C++
Klasse und StrukturJeder C-Programmierer kennt die Struktur (ein PASCAL-Programmierer würde es RECORD nennen). Es ist die Zusammenfassung mehrerer zusammengehöriger Datenelemente. Ein einfaches typisches Beispiel ist das Datum. Es besteht aus drei ganzen Zahlen. Ein C++-Programmierer wird eine Struktur als eine Klasse, die nur Datenelemente enthält und deren Elemente alle öffentlich zugänglich sind.
Funktionen als BestandteilEine Klasse kann neben Datenelementen auch Funktionen enthalten.
Die Funktionen gehören zur Struktur. Im Normalfall wird man auch zu Strukturen bereits Funktionen geschrieben haben, die darauf basieren. Neu ist, dass die Funktion mit der Klasse deklariert ist. Die Funktion braucht die Struktur nicht mehr als Parameter, da sie auf "ihr" Objekt wirkt.
Hier wird das Objekt Letztlich angelegt. Dieses Objekt wird durch die Neujahrsfunktion auf den 1.1.2000 gesetzt. Innerhalb der Funktion kann auf die Klassenelemente direkt zugegriffen werden. Man kann aber auch den vordefinierten Selbstreferenzzeiger this verwenden.
Konstruktor und DestruktorMan kann für eine Klasse eine Funktion schreiben, die immer ausgeführt wird, wenn das Objekt erzeugt wird. Dies nennt man den Konstruktor. Er sorgt dafür das die Elemente korrekt initialisiert sind und vermeidet dadurch Flüchtigkeitsfehler. Der Name der Funktion lautet wie die Klasse. Damit man merkt, dass das Datum noch nicht festgelegt wurde, kann man alle Elemente auf 0 setzen. Man könnte auch standardmäßig das heutige Datum eintragen, je nach Anwendung.
Der Konstruktor kann beispielsweise auch Speicher alloziieren, der für das Objekt gebraucht wird. Damit dieser Speicher wieder zurückgegeben werden kann, gibt es einen Destruktor, der bei Zerstörung der Variablen gerufen wird. Übrigens schadet es nichts, im Destruktor auf alle Zeiger der Klasse ein delete anzuwenden, sofern dieser 0 ist oder auf einen gültigen Bereich zeigt. Der Name der Destruktorfunktion wird gebildet, indem eine Tilde (~) dem Klassennamen vorangestellt wird. Globale Variablen werden zum Programmstart angelegt und zum Programmende zerstört. Lokale Variablen rufen ihren Konstruktor bei Definition und werden bei Verlassen ihres Geltungsbereichs zerstört. new und deleteWird ein Zeiger auf eine Klasse definiert, wird mit dem new-Operator der Speicher für die Instanz alloziiert und der Konstruktor aufgerufen. Die angeforderte Variable wird mit delete wieder weggeräumt.Wird new auf ein Array angewandt, muss auch der Array-Aufruf delete[] erfolgen. Ansonsten würde nur der Speicher des Feldes freigegeben, aber die Elemente des Arrays nicht. PrivatsphäreMan kann den Zugriff auf die Elemente von aussen verhindern, indem man sie privat deklariert. Aber wozu soll das gut sein? Warum sollte man sich selbst (oder die Kollegen) behindern?Gehen wir bei der Datumsklasse davon aus, dass der Wochentag für das Datum berechnet werden soll. Da die Berechnung sehr oft gebraucht wird, soll der Wochentag in der Klasse zwischengespeichert werden, wenn er einmal berechnet wurde. Solange das Datum nicht geändert wird, braucht der Wochentag auch nicht geändert werden. Man muss nur daran denken, bei jeder Änderung des Datums den Wochentag für ungültig zu erklären, damit er neu berechnet wird, wenn er angefordert wird.
Zu diesem Zweck werden die Variablen Tag, Monat und Jahr als privat deklariert. Das Setzen wird durch Funktionen erreicht. Neben dem Setzen des Datums erklärt sie automatisch den Wochentag für ungültig. Die Wochentagsfunktion prüft, ob ein gültiger Wochentag vorliegt und berechnet ihn ggfs. neu. Die Funktion Tag(int) wird so benutzt, dass sie als Auslesen des Datums interpretiert wird, wenn 0 übergeben wird. Ansonsten wird der Wert gesetzt. In C++ kann man bei der Deklaration angeben, wie der Parameter interpretiert werden soll, wenn keiner verwendet wird. Wird die Funktion Tag() aufgerufen, wird also der Parameter Wert auf 0 gesetzt.
Vererbung in C++Wenn eine neue Klasse benötigt wird, die alle Eigenschaften einer anderen Klasse hat und nur spezielle Eigenschaften hinzufügt, kann die neue Klasse abgeleitet werden. Man spricht davon, dass sie die Eigenschaften der alten Klasse erbt.Im Beispiel setzen wir die Existenz der tDatum-Klasse voraus. Wir wollen nun Feiertage definieren. Ein Feiertag ist einerseits ein Tag wie jeder andere auch. Er hat einen Tag, Monat, Jahr und einen Wochentag. Es kann sein, dass dieser Tag berechnet werden muss. Dann kann die Datumsaddition verwendet werden. Hinzu kommt eine Bezeichnung. Vielleicht soll später noch hinzugefügt werden, ob es ein gesetzlicher Feiertag ist. Deklariert eine abgeleitete Klasse so:
Konstanten in C++In C gibt es keine symbolischen Konstanten, sondern man verwendet den Präprozessor mit der Anweisung #define.
Unter C++ wird eine Konstante definiert.
Der Vorteil des Mehraufwands beim Schreiben ist die Tatsache, dass nun der Name MAX dem Compiler und dem Debugger bekannt sind. Durch den #define wird einfach die Zahl 1245 in den Code eingefügt. Virtuelle Funktionen in C++Virtuelle Funktionen sind Funktionen einer Basisklasse. Eine abgeleitete Klasse kann sie überschreiben und übernimmt damit die Ausführung der Funktion für ihre Klassenmitglieder. Das ist nichts Neues, dass funktioniert auch mit nicht virtuellen Klassen. Das Besondere an der virtuellen Klasse ist, dass das Objekt selbst weiss, zu welcher abgeleiteten Klasse es gehört und seine zugehörige Klassenfunktion ruft.Im Beispiel haben wir eine Klasse Bass und eine abgeleitete Klasse Tuba. Eine Tuba ist eine Spezifizierung eines Basses. Alle Bässe können dröhnen. Aber eine Tuba dröhnt in einer ganz besonderen Art. Wurde ein Objekt einmal als Tuba erstellt, wird es immer wie eine Tuba dröhnen, auch wenn es einfach nur als irgendein Bass eingesetzt wird.
In beiden Fällen wird Tuba ausgegeben. Das zeigt, dass das Objekt "weiß", dass es eine Tuba ist, auch dann, wenn es über einen Basszeiger zugegriffen wird. Das ist anders, wenn das Schlüsselwort virtual in der Klasse Bass entfernt wird.
VTableRealisiert wird dies durch die VTable, die jede Klasse mit virtuellen Funktionen und die abgeleiteten Klassen erstellen. In der VTable wird jede virtuelle Funktion aufgeführt. Wird die Klasse abgeleitet, erhält sie eine Kopie der VTable. Für alle selbstvorhandenen Funktionen wird der Zeiger umgeleitet.Bei jedem erzeugten Objekt ist ein Zeiger auf die VTable seiner Klasse enthalten. Wird mit einem beliebigen Zeiger auf diese Klasse zugegriffen, wird die VTable durchlaufen und so die richtige Funktion gefunden. Das bedeutet, dass jedes Objekt selbst weiss, was es ist und welche Funktion zu ihm gehört, egal über was für einen Zeiger es aufgerufen wird. Da das Objekt den Zeiger auf die VTable hat, sind virtuelle Funktionen nur bei nicht-statischen Elementen sinnvoll und erlaubt. Virtueller DestruktorEs gibt eine Faustregel, die besagt, dass jede Klasse mit virtuellen Funktionen auch einen virtuellen Destruktor haben soll. Wie oben gesehen, wird die abgeleitete Klasse Tuba auch durch einen Basszeiger angesprochen. Wird über diesen ein delete ausgeführt, würden bei einem nicht virtuellem Destruktor nur die Bestandteile des Bass, aber nicht der Tuba angesprochen. In diesem einfachen Beispiel ist das nicht relevant. Würde die Tuba aber gesonderte Speicherbereiche brauchen, dann würde diese über einen nichtvirtuellen Destruktor nicht aufgelouml;st.Da ein nicht virtueller Destruktor nicht gewährleistet, dass abgeleitete Klassen ordnungsgemäß abgebaut werden, kann ein nicht virtueller Destruktor sogar so interpretiert werden, dass der Autor ein Ableiten seiner Klasse nicht vorgesehen hat und wohl auch nicht empfielt. Abstrakte BasisklasseIm Beispiel kann man ein Objekt der Basisklasse erzeugen, einen Bass. Es kann aber sein, dass der Bass nur die Eigenschaften bestimmter Instrumente beschreiben soll. Einen realen Bass als solchen gibt es ja schliesslich nicht. Man kann die ableitende Klasse dazu zwingen, ihre eigene Realisierung von droehn zu liefern. Dazu setzt man die virtuelle Funktion in der Basisklasse gleich Null.
Damit wird die Basisklasse zur abstrakten Basisklasse. Es kann nur noch Ableitungen geben, keine Instanzen. Damit beschreibt die Klasse Bass eine Kategorie von Instrumenten, die gemeinsame Eigenschaften haben. Ein Bass als solcher existiert nicht wirklich. Es gibt aber eine Tuba, eine Bassgitarre, eine Bassstimme oder Basstrommel. Und jeder von diesen Basstypen kann eines: dröhnen. Die Klasse Bass kann als Schnittstelle zu allen abgeleiteten Klassen dienen und als ein solches Interface wird sie auch in der COM-Technologie verwendet. Das Interface legt den Vertrag fest, den die Ableitungen zu erfüllen haben. Überladene Funktionen in C++Überladen von FunktionenC++ unterscheidet Funktionen nicht nur am Funktionsnamen, sondern auch an den Parametern. Damit ist es möglich, mehrere Funktionen gleichen Namens zu haben, sofern sich die Parameter unterscheiden.Beispielsweise kann es die Funktion print für verschiedene Datentypen geben.
Der Compiler wird im obigen Fall die print-Funktion verwenden, die zum Typ von variable passt. Überladen von OperatorenAuch Operatoren können überladen werden. Damit können Operatoren verwendet werden. Beispielsweise ist es sinnvoll, bei einem Datum eine Addition ganzer Zahlen zuzulassen. Die Deklaration und Definition sieht etwa so aus:
Die Addition liefert ein neues Datum zurück und als rechter Operand ist eine ganze Zahl zulässig. So kann das Datum durch einfache Addition um 14 Tage weitergeschoben werden.
Das Überladen von Operatoren hat seine Tücken, wenn die Semantik nicht mehr so eindeutig ist, wie im obigen Fall. Templates in C++Templates werden verwendet, wenn gleiche Aktionen auf unterschiedlichen Datentypen anfallen.Template-FunktionenZunächst sollen Template-Funktionen betrachtet werden. Es sind Funktionen mit einer Funkionalität, die auf verschiedenen Datentypen gleich sind. Damit sind sie prädistiniert als Ersatz für die C-Makros. Diese konnten vor allem keine Typsicherheit gewährleisten und waren mehr oder weniger auf eine Zeile beschränkt.Als Beispiel dient die Funktionen min:
Die Templatefunktion instanziiert erst bei Benutzung. Es wird nicht explizit angegeben, daß z. B. eine char-Instanz erzeugt werden soll. Der Aufruf sähe beispielsweise so aus:
Templatefunktionen gewährleisten im Gegensatz zu den C-Makros, daß alle gleichen Typen auch gleich bleiben. Das min aus einem long und einem char zu bilden, ist also nicht möglich. Als Abschlußbeispiel die Funktion swap. Die Kopie der ersten Variablen wird durch den Konstruktor erzeugt. Die Parameterübergabe erfolgt per call by reference.
Template-KlassenWenn Gleichartigkeit über eine Funktion hinaus geht, wird die template-Klasse interessant.Als sehr einfaches Beispiel soll hier eine Billigimplementation eines Stacks dienen. Realisisiert wird der Stack mittels einem Array.
Die Template-Klasse realisiert das Verhalten unabhängig vom verwendeten Datentyp. Die Definition von T dient als Platzhalter für den Datentyp. Eine Instanziierung erfolgt durch
Hier wird erst angegeben, welchen Datentyps der Inhalt ist. Statt int könnte jeder andere Datentyp verwendet werden. Template-Klasse mit ParameterIm Beispiel oben haben wir die Stackgröße mit 256 festgelegt. Zwischen den eckigen Klammern kann ein solcher Wert als Parameter übergeben werden, im folgenden Beispiel als size. Dieser Wert muß bei der Instanziierung als Konstante angegeben werden.
Eine Instanziierung erfolgt durch
|
Autoren:04150
Artikel:00819 Glossar:04124 News:13569 Userbeiträge:16268 Queueeinträge:05150
Projektsteuerung statt Anwesenheitskontrolle
MONDroid - your monitoring Solution for Android with PRTG-Support Red-Hosting jetzt mit erweitertem Shop-Webhosting-Angebot [Mehr News]
Ihre Anforderungen an ein Online-Zeiterfassungs-Produkt?
|