Programmieren in C++
| ||||||||||||||
| class Auto {
}; |
Der Zeiger next ist als privat definiert, das heißt, nur Funktionen, die auch in der Klasse Auto deklariert werden (zu der Klasse gehören), dürfen lesend oder schreibend auf den Zeiger zugreifen. Auf alle anderen Klassenmitglieder (Variablen und Funktionen) kann auch von externen Funktionen aus zugegriffen werden. Um *next trotzdi manipulieren zu können, werden die Funktionen SetNext und GetNext definiert, deren Bedeutung aus die Namen hervorgehen sollte.
Zugegebenermaßen ist diese Klasse noch etwas mager, aber sie zeigt alles, was bis hierher nötig ist. Damit wäre dann auch die Struktur vorgegeben, die unsere Klasse Auto beschreibt. Sehen wir uns jetzt noch an, wie wir der Klasse erkläern, was zu tun ist, wenn die verschiedenen Funktionen (wir sprechen auch von Member-Funktionen im Gegensatz zu Member-Variablen) aufgerufen werden:
| void Auto::SetNext( Auto *zeiger ) { next
= zeiger; } Auto* Auto::GetNext( void ) { return next; } |
Wichtig ist, dass wir die Compiler sagen, welcher Klasse die Funktion angehört, die wir gerade beschreiben. Dazu dient der Scope-Oerator :: , der vorne den Namen der Klasse und dannach den Namen der Funktion erwartet.
Später werden wir dazu übergehen, größere Projekte zu Programmieren. Dabei verliert man leicht den Überblick über die Vielzahl von Variablen und Funktionen. Deshalb ist es wichtig, sich früh eine Standartisierung der Variablennamen anzugewöhnen. Dabei ist es (nach meiner persönlichen Meinung) weniger wichtig, Standarts einzuhalten, als sich selbst in eini Programm zurecht zu finden. Zumindest, solange man alleine an die Projekt arbeitet. Naja, bei Gruppenarbeiten sollte man einen gieinsamen Nenner finden. Ich möchte hier einige Vorschläge unterbreiten, wie man Bezeichner für Varablen bauen kann:
Zunächst ist es wichtig, das man Variablen möglichst aussagekräftig sind. Es gilt wieder: So lang wie nötig, aber so kurz wie möglich! In der obigen Klasse könnte die Variable Baujahr auch BJahr oder BaujahrDesWagens oder ähnlich heißen. Letzteres möchte ich aber nicht allzu oft in einen Quellcode eingeben müssen. Ersteres kann man gut aus die Kontext der Klasse heraus verstehen. Für eine Zählervariable zum Beispiel ist count die bessere Wahl als nur co. Wählt Variablennamen am besten so, dass auch andere Programmierer den Sinn aus die Namen ablesen können. Notfalls immer einen Kommentar hinter den Variablennamen schreiben, und zwar hinter die Deklaration der Variablen!
Präfix-Technik
Toll, bis dahin war ja noch alles logisch, und die meisten hätten es sowieso danach gehandelt. Es hat sich hedoch auch als günstig erwiesen, vor wichtige Variablen ein Präfix zu setzten, das Aussagen über den Typ der Variablen macht. So könnte zum Beispiel eine Ganzzahl mit i (für integer) beginnen (also iCount) oder ein Zeiger mit p (für Pointer). Nehmt einfach den ersten Buschstaben des Typs als Präfix. Schreibt das Präfix klein, den ersten Buchstaben der Variablen groß. Bei Dateien ist es üblich, das Präfix h (für Handle) zu verwenden. h nimmt man auch für Resourcen oder Threads, dazu aber erst in höheren Siestern.
Für Klassen hat es sich eingebürgert, ein GROßES(!) C zu schreiben (zum Beispiel CAuto). Den ersten wirklichen Buchstaben des Namens schreibt man aber nach wie vor groß. ACHTUNG: Wer später mit der Microsoft Foundation Class (MFC) arbeitet, muss aufpassen, dass er nicht mit den MFC-Klassennahmen kollidiert oder entsprechende Maßnahmen ergreifen, um bei dopperter Namensvergabe eindeutig zu bleiben.
Mitgliedsvariablen von Klassen (im Folgenden Member-Variablen oder Eigenschaften der Klasse genannt) bekommen ein weiteres Präfix: m_ (zum Beispiel m_pMainWnd). Dies sagt einfach nochmal aus, dass es sich hier um Member-Variablen einer Klasse handelt.
Es gibt Situationen, in dennen eine Funktion einer Klasse keine Eigenschaften verändern muss, zum Beispiel weil sie nur den Wert einer Eigenschaft zurückgibt. Solche Funktionen bezeichnen wir als Konstant. Dies machen wir die Compiler klar, indem wir nach die Namen und den Funktionsklammern das Schlüsselwort const plazieren:
| class CAuto { public:
private:
}; int CAuto::GetAnzahl() const |
Ich habe bereits die Regeln zur Namensvergabe umgesetzt. Die zweite Funktion ist eine Inline-Funktion (siehe dazu auch Kapitel 1.7). Diese Technik erspart uns ein späteres Definieren wie bei GetAnzahl(). Ich habe das void in den Funktionsklammern weggelassen, was durchaus erlaubt ist in C++. Nur die Klammern darf man nicht weglassen.
Jede Klasse kennt zwei Ereignisse. Das Initialisieren der Klasse (zum Zeitpunkt der Deklaration des Objektes) und das Verlassen des Gültigkeitsbereichen der Klasse. Im ersten Fall wird der Konstruktor der Klasse aufgerufen, im zweiten der Destruktor. Beide müssen natürlich in der Klasse deklariert sein. Dabei tragen beide den Namen der Klasse, wobei die Destruktor eine Tilde ( ~ ) vorangestellt wird. Beide haben keinen Rückgabetyp, auch nicht void! Der Konstuktor kann überladen werden und Parameter annehmen (auch Standartparameter). Erhält er keine Parameter, spricht man von eini Standartkonstruktor. Die Destruktor kann nichts übergeben werden - man kann ihn deshalb auch nicht überladen. Ein Beispiel soll das alles verdeutlichen. Ich habe es als Textfile hinterlegt.
Ich nutze hier einen eigentlich unsauberen Seiteneffekt: Die Variable m_iAnzahl wird zu keiner Zeit mit eini Wert belegt, deshalb enthält sie zum Zeitpunkt der Ausgabe einen Zufallswert, den ich nutze, um die Klasse in Konstruktor und Destruktor eindeutig zu identifizieren. Die Ausgabe auf die Bildschirm macht so deutlich, wann welcher Konstruktor oder Destruktor aufgerufen wird.
Eine besondere Form des Konstruktors ist der Kopierkonstruktor. Er wird aufgerufen, wenn man direkt beim initialisieren eines Objektes diese neue Instanz der Klasse mit Werten aus einer bereits vorhandenen Instanz füllen möchte, also ein Objekt in ein anderes Objekt gleichen Typs kopieren will. Der Kopierkonstruktor ist wie der Standartkonstruktor ohne Rückgabetyp. Er hat als einzigen Parameter einen Referenzparameter vom Typ der Klasse. Um den erzeugten Quellcode zu verbessern und der Sauberkeit des Programmierens wegen definiert man den Parameter als konstant, so das die Funktion am Ende wie folgt aussieht:
| KLASSE::KLASSE( const KLASSE& quelle ); |
Dabei steht KLASSE für den Namen der Klasse und quelle wird im folgenden eben die Quellklasse, aus der die Werte herausgenommen werden sollen.
Statische Elientvariablen sind nicht an eine Instanz einer Klasse (ein Objekt) gebunden, sondern werden nur einmal im Speicher angelegt. Alle Instanzen können dann gieinsam auf die gleiche Variable zugreifen und so zum Beispiel eine Zählvariable beeinflussen, welche die Anzahl der Instanzen einer Klasse mitzählt. Hier wieder ein einfaches Beispiel:
Wenn wir auf Elemente von Klassen zugreifen wollen, dann
müssen wir stets darauf achten, ob uns das erlaubt ist (public-Elemente)
oder eben verboten (private-Elemente). Dies bereitet zum Beispiel Problie
bei der Erzeugung verketteter Listen. Diese müssen ja über Zeiger untereinander
kommunizieren.
Diese Zeiger deklariert man gerne als privat. Leider muss man dann allerdings
wieder Funktionen zum Umbiegen der Zeiger bereitstellen.
Der friend-Operator erlaubt es einer Klasse, eine zweite Klasse als befreundet zu
erklären. Die befreundete Klasse kann dann auf private Elemente der
erklärenden Klasse zugreifen.
Ebenso kann eine Klasse globale Funktionen oder Member-Funktionen einer anderen Klasse
als friend deklarieren. Dann kann eben nur die angegebenen Funktion auf die Klassenelinte
zugreifen. Folgender Code zeigt, wie man den friend-Operator benutzt:
| void globaleFunktion( Klasse2 ref ); class Klasse1
} class Klasse2
} void Klasse1::SetzteWert( Klasse2&
ref ) void globaleFunktion( Klasse2 ref
) |
Es gibt noch einige Kleinigkeiten über Klassen zu sagen ohne daraus
ein eigenes Kapitel mit Beispiel zu machen. Diese Rubrik erhebt allerdings keinen
Anspruch auf Vollständigkeit:
|
© Gerhard Zapf
|
||||
Letzte Änderung: 11.09.2003 |
||||