Programmieren in C++
Kapitel 1: Nichtobjektorientierte Erweiterungen zu C

Zwischen ANSI-C und C++ gibt es einige unterschiede, die nicht (zumindest nicht offensichtlich) auf objektorientiertes Programmieren zurückzuführen sind. Ich möchte hier auf einer Art und Weise auf die wichtigsten Dinge eingehen, welche nicht unbedingt Formal richtig ist, sondern vor allem für Anfänger verständlich. Man sehe mir also bitte die Behauptung nach, cin und cout wäre wenig objektorientiert.

Ich sehe dieses Kapitel als Brücke zwischen C und C++, nicht mehr aber auch nicht weniger. Einiges könnte genauerbehandelt werden - wird es aber nicht!

1.1 Namensräume

Wir haben in C schon Header-Files kennengelernt. Diese enthalten (grob gesagt) Funktionen zu bestimmten bereichen, wie zum Beispiel der Ein- und Ausgabe (stdio.h) oder mathematische Funktionen (z.B. math.h). Wir können solche Header-Dateien selbst schreiben, und es kann auch vorkommen, dass wir eine Funktion in verschiedenen Header-Dateien mehrmals schreiben (vielleicht um leicht unterschiedliche Arten der selben Funktion zu implementieren). Dann müssen wir dem Compiler allerdings mitteilen, welche Version der Funktion er im zweifelsfall verwenden soll.

Ich erspare mir Details und bringe ein Beispiel, die für den ganzen Kurs reichen wird (hoffe ich!):

#include <iostream>
using namespace std;
gleichbedeutend ist (zumindest in unserem Fall):

#include <iostream.h>

Ich habe mich spontan entschieden, bei der herkömmlichen Methode zu bleiben!

1.2 Ein- und Ausgabe von Zeichen

Es gibt zwei neue Funktionen um Daten auszugeben und einzulesen. Sie tragen dem Modell rechnung, dass Daten als Datenströme von der Tastatur kommen und auch als Ströme zum Bilschirm gelangen (analog später auch auf Dateien anwendbar). Jetzt raten wir mal, woher die Datei iostream.h ihren Namen hat.

Ich will die Funktionsweise der beiden Funktionen cin und cout anhand eines Beispiels zeigen. Zusätzlich zu der hier gezeigten Verwendung beider Funktionen gibt es noch viele Möglichkeiten, mit cin und cout zu arbeiten. Diese werde ich im Laufe des Kurses einfüren, wenn sie nötig sind!

#include <iostream.h>

void main( void )
{

int zahl = 5, eingabe;

cout << "Geben Sie bitte eine Zahl ein: ";
cin >> eingabe;

// Das endl entspricht dem Steuerzeichen \n in ANSI C!
cout << zahl << " * " << eingabe << " = " << zahl*eingabe << endl;

}

Ein- und Ausgabe wird in C++ nichtmehr als Funktion im eigentlichen Sinne behandelt. Vielmehr handelt es sich um Ein- und Ausgabeströme von und zu den Geräten. Der Stream-Operator zeigt an, in welche Richtung der Transfer vonstatten geht. Ströme können auch geschachtelt werden, wie die letzte Zeile zeigt. Um einen Zeilenumbruch zu erzeugen, verwendet man die endl-Marke.

Wichtigste Neuerung ist, dass cin und cout den Typ der übergebenen Variablen selbst erkennen. Es werden keine Formatangaben mehr benötigt!


1.3 Datentypen

Die Datentypen in C++ entsprechen denen von ANSI-C, nur das C++ eine stärkere Typbindung durchfürt! Zusätzlich enthält C++ den Datentyp bool, welcher Wahrheitswerte (true oder false) enthält. Dabei entspricht false dem Wert 0, alle anderen Werte sind true!


1.4 Lokal definierte Variablen

In C habe ich am Rande erwähnt, dass Anweisungen zu Blöcken zusammengefasst werden können, indem man mit den Blockklammern { und } arbeitet. In C++ ist es möglich, Variablen innerhalb dieser Blockklammern zu definieren. Ihr Gültigkeitsbereich ist dann auf das Innere des Blockes begrenzt, in dem sie definiert sind. Wir können so zum Beispiel Variablen direkt in einer For-Schleife definieren:
{
for ( int c = 1; c<= 10; c++ ) { cout << "Zahl: "<< c << endl; }
}
cout << c*10 << endl;

Die Variable c kann innerhalb der grünen Klammern problemlos verwendet werden, die rote Zeile produziert allerdings einen Fehler, weil c an dieser Stelle nicht mehr definiert ist.


1.5 Referenzen

In C haben wir gelernt, dass man Parameter, deren Wert wir innerhalb einer Funktion verändern wollen, als Zeiger übergeben müssen. In C++ kann man stattdessen Referenzen auf den Parameter anlegen:
#include <iostream.h>

void drucke( int& parameter ) { cout << parameter << endl; parameter *= 2; }

void main( void )
{

int parameter = 10;
int k = 1;
int& r = k;

k = 2;
drucke( r );

r = 4;
drucke( k );

cout << r << " " << k << endl;

drucke( k );
cout << i << endl;

}



Bevor ihr das Programm abtippt und ausprobiert, denkt mal darüber nach, welche Ausgabe am Bildschirm erscheinen sollte.

Wichtig:Werden Variablen als Referenz deklariert (wie r im obigen Beispiel), dann muss bei der Deklaration SOFORT angegeben werden, auch welche Variable verwiesen werden soll (im obigen Beispiel k)!

1.6 Standartparameter (default-Werte für Parameter)

Man kann in C++ auch Standart-Werte fü Parameter angeben. Diese Parameter müssen dann in der Parameterliste der Funktion am Ende stehen. Werden die Parameter beim Funktionsaufruf weggelassen, dann verwendet die Funktion die Standartparameter.

Auch Referenzparameter können mit default-Werten vorbelegt werden. Wie das geht, zeigt alles das folgende Beispiel:
#include <iostream.h>

// Eigentlich sollte man auf globale Variablen verzichten
// hier sind sie zur Demonstration nötig!

int u = 4, v = 5;

void drucke( int a, int b, int &c, int& d = u, int e = 5, int&f = v )
{ cout << a << b << c << d << e << f << endl; }

void main( void )
{

int w = 9, x = 3, y = 1, z = 8;
drucke( u, v, w, x, y, z );
drucke( u, v, w );
drucke( u, v, w, x );
drucke( u, v, w, x, y );

}


1.7 Inline - Funktionen / Abkürzungen

An dieser Stelle gehe ich auf einen Typ von Funktionen ein, der uns VIEL(!) später helfen wird, unsere Programme zu beschleunigen. Normalerweise liegen Funktionen irgendwo im Speicher. Wird die Funktion aufgerufen, setzt der Prozessor seinen Programm-Zeiger (dieser zeigt auf die jeweils nächste Zeile im laufenden Programm) auf diese Speicherstelle. Zuvor werden jedoch verschiedene Prozessorinterne Werte auf den Stapelspeicher gesichert. Dieses sichern der sogenannten Prozessumgebung ist jedoch sehr Zeitaufwendig, so dass es unter umständen schneller geht, eine Funktion direkt in das Hauptprogramm einzubinden.

Um die Funktion jedoch trotzdem nur einmal schreiben zu müssen, definiert man sie als Inline-Funktion. Diese sind genauso wie gewöhnliche Funktionen zu programmieren, werden jedoch vom Compiler beim compilieren an die jeweiligen Positionen in die aufrufende Funktion hineingeschrieben, somit entfällt das Springen und das Sichern der Umgebung.

Ich möchte mit dem folgenden Beispiel noch mehr zeigen: Es gibt in C die Mölichkeit, if-Abfragen abzukürzen. Dazu schreibt man zunächst die Bedingung (z.B. a != b), dann ein ?, dann die THEN- Anweisung, gefolgt von einem : und der ELSE- Anweisung. Ein Beispiel macht dies deutlicher:

#include <iostream.h>

inline int max( int a, int b)
{
return a > b ? a : b; }

void main( void )
{

int a= 5, b = 6;
cout << "Maximum von a un b: " << max( a,b ) << endl;

}


Die Funktion max kann auch anders implementiert werden:

inline int max( int a, int b)
{

int ergebnis;
if( a > b) ergebnis = a; else ergebnis = b;

return ergebnis;

}



1.8 Überladene Funktionen

Es besteht die Möglichkeit, zwei Funktionen mit gleichem Namen zu implementieren. Dies gelingt uns deshalb, weil in C++ nichtmehr nur der Name, sondern auch die Übergabeparameter zur Idendifizierung einer Funktion herangezogen werden. So lässt sich zum Beispiel eine Funktion Quadrat schreiben, die einmal Ganzahlwerte und einmal Gleitkommawerte aufnimmt. Die Anzahl der Parameter von überladenen Funktionen muss nicht gleich sein! Ich will wieder en kurzes Beispiel bringen, welches den Sachverhalt ausreichend erläutert:

#include <iostream.h>

void swap( double& m, double& n );
void swap( const char*& a, const char*& b );

void main( )
{

// Hier handelt es sich um Zeiger auf
// Zeichenkonstanten der Zeiger kann
// verändert werden, der Inhalt nicht!

const char
*s = "eins";
const char *t = "zwei";

double x = 1.0, y = 2.0;

swap( x, y );
swap( s, t );

cout << x << " " << y << endl;
cout << s << " " << t << endl;

}

void swap( double& m, double& n )
{
double z = m; m = n; n = z; }

void swap( const char*& a, const char*& b )
{
const char *z = a; a = b; b = z; }



1.9 Speicherplatzreservierung

Die Änderungen auf diesem Gebiet sind so trivial, dass ich mir ein Beispiel spare. Wir eretzen einfach malloc durch new und lassen die Parameter weg. New braucht keine Angabe über die Größe des zu reservierenden Speichers mehr.

Statt den Speicher mit free wieder zu löschen geben wir ihn jetzt mit delete wieder frei.

int *zeiger = new int;
delete zeiger;

Das Beispiel legt einen Zeiger auf eine Integer-Variable an und reserviert gleich den Speicherplatz dafür. New benötigt also lediglich den Typ, auf den der Zeiger verweist. Gleich darauf wird der Speicher wieder freigegeben. Bitte verzichtet darauf, den Sinn dieses Beispiels zu hinterfragen. Danke.


© Gerhard Zapf
Kapitelübersicht
Sitemap
www.tutorialpage.de
Objektarbeit
Diese Seite wurde seit dem 12.07.01 genau 455 mal abgerufen.
Letzte Änderung: 11.09.2003