|
I-PROGR2 SS03 - PROGRAMMIEREN2 - Vorlesung mit Übung
|
Das Ziel der heutigen Vorlesung besteht darin, aufbauend auf dem Wissen über die Sprache C einige wichtige Unterschiede von C++ gegenüber der Sprache C bekannt zu machen, die noch nicht notwendigerweise ein spezielles Wissen über objektorientiertes Programmieren voraussetzen. Die objektspezifischen Elemente werden dann ab der nächsten Vorlesung nach und nach eingeführt.
Für die Erstellung von C++-Quellkode wird standardmässig wieder der Editor xemacs benutzt. Zum Kompilieren der GNU-C++-Compiler g++. Eine C++-Quelldatei ('source file') hat die Endung '.cpp'.
Von der Sprache C her ist das Sprachmittel 'mehrdimensionales Feld' bzw. 'mehrdimensionaler Array' bekannt. Im 1-dimensionalen Fall stellt ein Array eine Folge von Elementen des gleichen Typs im Speicher dar, wobei man auf jedes Element entweder mittels des Array-Index oder mittels seiner Adresse zugreifen kann.
In der Sprache C++ gibt es eine ganze Reihe neuer Sprachelemente, die durch die Standardbibliothek STL ('standard library') zur Verfügung gestellt werden (Das Konzept der Standardbibliothek wird zu einem späteren Zeitpunkt noch ausführlicher besprochen). Eines der neuen Sprachmittel ist das vector-Template. Hier einige Beispiele, welche Funktionalitäten das vektor-Template zur Verfügung stellt.
Hier eine Auswahl der verfügbaren Funktionen:
vector<TYPE> c |
Creates empty vector c with no elements |
vector<TYPE> c1(c2) |
Creates a copy of vector c2 into c1 (same type) |
vector<TYPE> c(n) |
Creates vector c with n elements |
vector<TYPE> c(n,elem) |
Creates vector c with n copies of element elem |
vector<TYPE> c(beg,end) |
Creates a vector c initialized with the elements of the range [beg,end) |
c.~vector<TYPE>() |
Destroys all elements and frees the memory |
c.size() |
Returns the actual number of elements |
c.empty() |
Returns whether the vector is empty c |
c.max_size() |
Returns the maximum number of elements possible |
capacity() |
Returns the maximum number of elements possible without reallocation |
reserve() |
Enlarges capacity if not enough |
c1 == c2 |
Whether equal |
c1 != c2 |
Not equall |
c1 < c2 |
Lesser than |
c1 > c2 |
Greater than |
c1 >= c2 |
greater than or equal |
c1 <= c2 |
lesser than or equal |
c1 = c2 |
Assigns all elements of c2 to c1 |
c.assign(n,elem) |
Assigns n copies of elem to c |
c.assign(beg,end) |
Assigns the elements of the range beg - end |
c1.swap(c2) |
Swaps the data of c1 and c2 |
swap(c1,c2) |
Swaps the data of c1 and c2 |
c.at(idx) |
Returns the element with index idx. In case of 'out of range' throws an error (the only range checking function !) |
c[idx] |
Returns element with index idx (no range checking!) |
c.front() |
Returns the first element (no range checking!) |
c.back() |
Returns the last element (no range checking!) |
c.push_back(elem) |
Appends a copy of element elem at the end |
c.pop_back() |
Removes the last element (does not rrturn it) |
c.erase(pos) |
Removes the element at position pos and returns position of next element |
c.resize(num) |
Changes the number of elements to num; new elements are created by the default constructor |
c.resize(num,elem) |
Changes the number of elements to num; new elements are created as copies of elem |
c.clear() |
Removes all elements |
Dazu ein einfaches Beispiel, das nur einen winzigen Bruchteil der verfügbaren Möglichkeiten nutzt.
//--------------------------- // // ArrayDemo.cpp // // Author: m.a.weiss // Modifications: g.doeben-henisch // // compilation: g++ -o ArrayDemo ArrayDemo.cpp // usage: ArrayDemo // //--------------------------------- #include <iostream> #include <vector> using namespace std; // Generate numbers (from 1-100) // Print number of occurrences of each number int main( ) { const int DIFFERENT_NUMBERS = 100; // Prompt for and read number of games. int totalNumbers; cout << "How many numbers to generate?: "; cin >> totalNumbers; vector<int> numbers( DIFFERENT_NUMBERS + 1 ); // Initialize the vector to zeros. for( int i = 0; i < numbers.size( ); i++ ) numbers[ i ] = 0; // Generate the numbers. for( int j = 0; j < totalNumbers; j++ ) numbers[ rand( ) % DIFFERENT_NUMBERS + 1 ]++; // Output the summary. for( int k = 1; k <= DIFFERENT_NUMBERS; k++ ) cout << k << " occurs " << numbers[ k ] << " time(s)\n"; return 0; }
//--------------------------- // // GetInts.cpp // // Author: m.a.weiss // Modifications: g.doeben-henisch // // compilation: g++ -DRESIZE[=0] -o GetInts GetInts.cpp // usage: GetInts // //--------------------------------- #include <iostream> #include <vector> using namespace std; #if RESIZE // Read an unlimited number of ints with no attempts at error // recovery; fill the vector parameter with the data; its size // after the return tells how many items were read. void getInts( vector<int> & array ) { int itemsRead = 0; int inputVal; cout << "case cin " << RESIZE << endl; cout << "Enter any number of integers: "; while( cin >> inputVal ) { if( itemsRead == array.size( ) ) array.resize( array.size( ) * 2 + 1 ); array[ itemsRead++ ] = inputVal; } array.resize( itemsRead ); } #else // Same as before, but use push_back void getInts( vector<int> & array ) { int inputVal; array.resize( 0 ); cout << "Case push-back" << RESIZE << endl; cout << "Enter any number of integers: "; while( cin >> inputVal ) array.push_back( inputVal ); } #endif // Test getInts. int main( ) { vector<int> array; getInts( array ); for( int i = 0; i < array.size( ); i++ ) cout << array[ i ] << endl; return 0; }
Neben dem Konzept Zeichenkette ('string'), wie es aus der Sprache C bekannt ist --C-String genannt--, gibt es in C++ auch eine neue Klasse 'String'.
Hier einige ihrer Funktionen:
Hier eine Auswahl der verfügbaren Funktionen:
string c |
Creates empty string c with no elements |
string c1(c2) |
Creates a copy of string c2 into c1 |
string c(n,a) |
Creates a string c with n copies of character a |
string c(beg,end) |
Creates a string c initialized with the characters of the range [beg,end) |
c.~string() |
Destroys all elements and frees the memory |
c.size() |
Returns the actual number of elements |
c.empty() |
Returns whether the string is empty c |
c.max_size() |
Returns the maximum number of elements possible |
c.length() |
Returns the number of elements |
c.reserve() |
Enlarges capacity if not enough |
c1 == c2 |
Whether equal |
c1 != c2 |
Not equall |
c1 < c2 |
Lesser than |
c1 > c2 |
Greater than |
c1 >= c2 |
greater than or equal |
c1 <= c2 |
lesser than or equal |
c1 = c2 |
Assigns all elements of c2 to c1 |
c.assign(n,elem) |
Assigns n copies of elem to c |
c.assign(beg,end) |
Assigns the elements of the range beg - end |
c1.swap(c2) |
Swaps the data of c1 and c2 |
swap(c1,c2) |
Swaps the data of c1 and c2 |
c.at(idx) |
Returns the element with index idx. In case of 'out of range' throws an error (the only range checking function !) |
c[idx] |
Returns element with index idx (no range checking!) |
c.getline() |
Read the value of a stream |
c.push_back(elem) |
Appends a copy of element elem at the end |
c1 + c2 |
Concatenates two strings |
c.resize(num) |
Changes the number of elements to num; new elements are created by the default constructor |
c.clear() |
Removes all elements |
c.c_str() |
Return the string as C-String |
c.data() |
Return the string as an array of characters without terminating 0. |
Hier ein einfaches erstes Beispiel:
//--------------------------- // // TestString.cpp // // Author: m.a.weiss // Modifications: g.doeben-henisch // // compilation: g++ -o TestString TestString.cpp // usage: TestString // //--------------------------------- #include <iostream> #include <string> using namespace std; int main( ) { string a = "hello"; string b = "world"; string c; // Should be "" c = a + " "; // Should be "hello " c += b; // Should be "hello world" // Print c the easy way. cout << "c is: " << c << endl; // Print c the primitive way. cout << "c is: " << c.c_str( ) << endl; // Print c character-by-character. cout << "c is: "; for( int i = 0; i < c.length( ); i++ ) cout << c[ i ]; cout << endl; return 0; }
Ein weiteres neues Sprachmittel in C++ ist die Referenz. Eine Referenz ist quasi ein 'Stellvertreter' für eine andere Variable; einmal vereinbart, kann man sie nicht mehr ändern. Im Kontext der Argumentübergabe an eine funktion arbeitet dieser Mechanismus wie die Übergabe einer Adresse, aber man benötigt weder den Inhalts-Operator '*' noch den Adressoperator '&'. Die Syntax ist sehr einfach:
TYPE1 bezug;
TYPE1 & referent = bezug;
//--------------------------- // // TestSwap.cpp // // Author: m.a.weiss // Modifications: g.doeben-henisch // // compilation: g++ -o TestSwap TestSwap.cpp // usage: TestSwap // //--------------------------------- <iostream> using namespace std; // Does not work void swapWrong( int a, int b ) { int tmp = a; a = b; b = tmp; } // C Style -- using pointers void swapPtr( int *a, int *b ) { int tmp = *a; *a = *b; *b = tmp; } // C++ Style -- using references void swapRef( int & a, int & b ) { int tmp = a; a = b; b = tmp; } // Simple program to test various swap routines int main( ) { int x = 5; int y = 7; swapWrong( x, y ); cout << "x=" << x << " y=" << y << endl; swapPtr( &x, &y ); cout << "x=" << x << " y=" << y << endl; swapRef( x, y ); cout << "x=" << x << " y=" << y << endl; return 0; }
Gegenüber C wurde das dynamische Speichermanagement vereinfacht. Es gibt nur noch die Operatoren 'new' und 'delete'.
TYPE Pointer = new TYPE |
Erzeugt mit dem Befehl 'new' einen Speicherbereich geeigneter Grösse vom Typ TYPE Dazu schreibt der Standard: "Entities created by a new-expression have dynamic storage duration (3.7.3). [Note: the lifetime of such an entity is not necessarily restricted to the scope in which it is created. ] If the entity is a non-array object, the new-expression returns a pointer to the object created. If it is an array, the new-expression returns a pointer to the initial element of the array." |
delete pointer |
Hebt mit dem Befehl 'delete' den Speicherbereich wieder auf, auf den der Pointer pointer zeigt; pointer muss zuvor von new erzeugt worden sein. |
Einige Beispiele zum Gebrauch von 'new' und 'delete':
float *fp = new float; //Erzeugt einen speicherbereich für eine Gleitkommazahl delete fp; //Gibt den Speicherbereich wieder frei char *s = new char[len+1]; //Legt ein Feld für Elemente vom Typ char an delete [] s; //Freigabe des Feldes mit den Elementen vom Typ char. Achtung: bei Feldern müssen vor dem Feldnamen die //eckigen Klammern '[]' gesetzt werden
Dazu das Beispielprogramm bzeile3.cc. Die Eingaben an das Programm bzeile3 werden komplett über die Befehlszeile abwickelt. Das Schema der Befehlszeile lautet:
bzeile3 YDIM XDIM SEED MAXRUN
Ein Aufruf von bzeile3 wäre dann z.B. der folgende:
bzeile3 10 10 123 100
Nachdem die Argumente an das Programm mit Namen 'bzeile3' übergeben worden sind, werden diese vom Programm zunächst automatisch in C-Strings umgewandelt und in dem Feld '*argv[]' gespeichert, d.h. jedes Element des Feldes ist ein C-STring. Da es sich bei der Eingabe aber um Zahlen handelt, müssen diese Zahlen im Stringformat in ein Zahlenformat umgewandelt werden. Dies geschieht im Programm mit der Funktion 'atoi()' (ASCII-to-Integer). Schliesslich muss noch das 2D-Feld mit der gewünschten Dimensionierung im Speicher angelegt werden. Dies geschieht mit 'new'. Ab dann läuft das Programm genauso wie das Programm rprojekt1.cc. Die Hilfsfunktionen in den Dateien rand1.cc, show2.cc sowie count2d.cc wurden nicht verändert. Hier ist der Quellcode zu bzeile3.cc:
//************************* // // bzeile3.cc // // DATUM: 12. April 2002 // ÄNDERUNG: 12. April 2002 // // You can give arguments on the commandline which are numbers and these will be counted, printed on the // screen, converted to numbers and then used to initialize a 2D-array and to guide the program. // The program runs a random number generator to hit the elements of a 2D-array. // After a given number of runs (maxrun) the program stops and counts the elements which have been not hit // by an random event // // compile: g++ -o bzeile3 bzeile3.cc rand1.cc show2.cc count2d.cc // usage: bzeile3 YDIM XDIM SEED MAXRUN // //******************************* #include <iostream> #include <cstdlib> #include "bzeile3help.h" int main(int argc, char *argv[]){ int rx,ry; //aktuelle Zufallszahlen für die Y-Richtung und für die X-Richtung eines 2D-Feldes float upper_x, upper_y; //Groesste Zufallszahl, die für die Y- bzw. X-Richtung erzeugt werden soll if (argc > 1) { std::cout << "Programm " << argv[0] << " hat " << argc-1 << " weitere(s) Argument(e)." << std::endl; for(int i=1; i<argc; i++){ std::cout << i << " = " << argv[i] << std::endl; } if (argc == 5){ //Argumentzahl stimmt; Inhalt ist nicht geprüft! const unsigned int yDim = atoi(argv[1]); //Setzen der y-Dimension const unsigned int xDim = atoi(argv[2]); //Setzen der x-Dimension const unsigned int seed =atoi(argv[3]); //Setze eine Initialisierungszahl für die Zufallszahlenfolge const unsigned int maxrun =atoi(argv[4]); //Maximale Zahle von gewuenschten Zufallsereignissen upper_x = xDim * 1.0; //Umwandlung von unsigned int in float upper_y = yDim * 1.0; int (*ap)[xDim] = new int[yDim][xDim]; //new generiert neuen Speicher; liefert einen Zeiger auf ein Feld zurück srand(seed); //Startet neue Zufallszahlenfolge for(int i=0; i<maxrun; i++) { //Hauptschleife ry = rand1(upper_y)-1; //Übernahme einer Zufallszahl für die y-Dimension rx = rand1(upper_x)-1; //Fürprechend fuer die x-Dimension std::cout << "y =" << ry << " , x = " << rx << std::endl; ap[ry][rx] ++; std::cout << "Runde "<< i << "-----------------------------------------" << std::endl; show2(&ap[0][0], yDim, xDim) ; for(int j=0; j<25000000; j++) {;} //Verzögerungsschleife, um Ereignisse 'sehen' zu koennen }//End of for i std::cout << "-------------------------------------------------------------------------------------------" << std::endl; std::cout << "Prozentsatz der NICHT besetzten Felder = " << ( count2d(&ap[0][0], yDim, xDim) * 1.0) / ((yDim * xDim * 1.0) / 100.0 ) << std::endl; std::cout << "-------------------------------------------------------------------------------------------" << std::endl; delete [] ap; //Hebt den Speicher wieder auf } } else { std::cout << "Programm " << argv[0] << " hat keine zusaetzlichen Argumente." << std::endl; } }
LD = g++ CPPFLAGS = -g objects = bzeile3.o rand1.o show2.o count2d.o bzeile3: $(objects) $(LD) -o bzeile3 $(objects) bzeile3.o: bzeile3help.h clean: rm bzeile3 $(objects)
//************************* // // show2.cc // // first: april-11, 2002 // last: april-11, 2002 // // Shows the content of a 2D-Array with the dimensions // yDim x xDim // // compile: g++ -c show2.cc // call: show2(&Array-Name[0][0], yDim, xDim) //******************************* #include <iostream> void show2(int *ap, int yDim, int xDim){ int x,y; //Indexvariablen für die for-Schleifen std::cout << "-------------------------------------------------------------------------"<< std::endl; for( y=0; y <yDim; y++) { for( x=0; x<xDim; x++){ std::cout << *(ap + (y * xDim) + x); }//End of x-loop std::cout << std::endl; }//End of y-loop }
/**************************** * * rand1.cc * * Ein Zufallsgenerator mit oberer Schranke und Anzahl der Würfe * * * COMPILATION: g++ -c rand1.cc * CALL: rand1(upper) * *********************************/ #include <cstdlib> int rand1 (float upper){ /* Da rand1() eine Zufallszahl r zwischen 1 und RANDS_MAX liefert, muss diese Zahl r * auf den gewünschten Bereich von [1,upper] umgerechnet werden. Die 'Numerical Recipes in C' * empfehlen die untenstehende Formel: */ return (1+(int)( upper*rand() /(RAND_MAX+1.0 ) )); }
//************************* // // count2d.cc // // first: april-11, 2002 // last: april-11, 2002 // // Counts the zeros in a 2D-Array with the dimensions // yDim x xDim // // compile: g++ -c count2d.cc // call: count2d(&Array-Name[0][0], yDim, xDim) //******************************* #include <iostream> unsigned int count2d(int *ap, int yDim, int xDim){ int x,y; //Indexvariablen für die for-Schleifen unsigned int counter; counter = 0; for( y=0; y <yDim; y++) { for( x=0; x<xDim; x++){ if (*(ap + (y * xDim) + x) == 0) counter++; }//End of x-loop }//End of y-loop return counter; }
Nachdem durch konkrete Beispiele einige der gegenüber C neuen Sprachelemente von C++ eingeführt worden sind, soll ab der nächsten sitzung des Konzept der objektorientierten Programmierung sowie dessen Realisierung mittels der Sprache C++ im vordergrund stehen.
Wie unterscheiden sich die Compileraufrufe von C und C++?
Was unterscheidet einen Vektor von einem Array?
Wie gibt man in C++ etwas auf den Bildschrimn aus?
Wie liest man in C++ etwas von der Tastatur in eine Variable?
Mit welchen Befehlen können Sie einen Vektor vergrössern oder verkleinern?
Wie kopieren Sie einen Vektor in einen anderen?
Wie können sie einen Vektor mit einem bestimmten Anfangswert initialisieren?
Wie können Sie in einem Vektor alle Elemente löschen ohne den Vektor zu löschen?
Wie beseitigen Sie einen Vektor?
Was unterscheidet einen String in C++ von einem C-STring?
Wie legen Sie in C++ Strings an?
Wie können Sie einen String initialisieren?
Wie verketten Sie zwei Strings c1 und c2?
Wie kopieren sie einen String c2 nach c1?
Wie können Sie Strings c1 und c2 vergleichen?
Was ist eine Referenz?
Konstruieren Sie ein Beispiel, in dem sie den Gebrauch von Pointern durch die Referenz ersetzen.
Welche Operatoren benutzt C++ für das dynamische Speichermanagement?
Wie reservieren Sie Speicher für eine einfache Variable vom Typ T?
Wie reservieren Sie Speicher für einen Array vom Typ T?
Wie reservieren Sie Speicher für einen 3-dimensionalen Array vom Typ T
Welche Alternative kennen Sie, wenn Sie Speicher für einen 2-dim Array reservieren wollen ohne mit zwei Dimensionen arbeiten zu wollen?
Wie ist ein einfaches Makefile aufgebaut?
Benutzen Sie das Makefile zum Beispielprogramm der dynamischen Speicherreservierung und löschen sie alle Objektfile.
P1: Schreiben Sie eine kleine Routine, bei der Sie über die Tastatur beliebig viele Zahlen eingeben können; benutzen sie für die Speicherung der Zahlen einen Vektor.
P2a: Schreiben Sie eine kleine Routine, bei der Sie über die Tastatur beliebig viele Worte eingeben können. Benutzen Sie zur Speicherung den C++-Datentyp 'string'. Nach Abschluss der Eingabe sollen die Worte sortiert und auf den Bildschirm ausgegeben werden.
P2b: Erweitern Sie das Programm P2a dahingehend, dass das Programm erkennt, wenn sich Worte in Buchstaben überlappen.Die Ausgabe der Liste soll so sein, dass (1) das Wort in der Liste ausgedruckt wird, dann (2) alle Worte, die sich mit diesem Wort überlappen.
P3: Schreiben Sie das Programm P2b so um, dass das Programm mindestens eine externe Funktion umfasst, deren Argumente Referenzen sind. Mittels der Referenzen sollen Variablen des Hauptprogramms manipuliert werden.