Montag, 28. Mai 2012

Buchempfehlung: Kalista, Heiko - C++ für Spieleprogrammierer (2009)

  • geeignet für: Anfänger
  • Erklärt alle wichtigen Aspekte der C++-Programmierung sehr verständlich
  • Bietet anschauliche Beispiele aus dem Bereich der Spieleprogrammierung
  • Leitet den Leser sehr gut von den grundlegenden Kenntnissen zum ersten selbst programmierten Spiel
ISBN: 978-3-446-42140-0 Preis: 34,90€

Randomizer

Der Randomizer (Zufallsgenerator) ist eines meiner ersten Projekte in C++. Mit ihm ist es möglich aus beliebig viele Eingaben eine zufällige auszuwählen. Wer möchte kann den Randomizer auch weiterentwickeln und an seine Bedürfnisse anpassen. Viel Spass beim Ausprobieren!

Update: 24.02.2013 - Bugfixes, kann nun mit beliebig vielen Eingaben umgehen
Update: 28.02.2013 - Bugfixes, verschiedene Versionen für Linux und Windows
Update: 09.05.2013 - kann nun auch Dateien lesen (Ein Element pro Zeile)

Downloads:
Download 1 (Sourcecode)
Download 2 (Programm Linux)
Download 3 (Programm Windows)

Sonntag, 27. Mai 2012

Die C++ Standard-Bibliothek

Die C++ Standard-Bibliothek stellt einige "Werkzeuge" zur Verfügung, mit deren Hilfe das Programmieren sehr viel einfacher wird. Einen Bestandteil dieser Library haben wir bereits in meinem ersten C++ Tutorial kennengelernt, nämlich die Datei iostream, welche durch #include eingebunden wurde. Neben der Ein- und Ausgabe von Text stellt die C++-Standard-Library noch 8 weitere Klassen bzw. Funktionen zur Verfügung. Die 9 Bestandteile sind:
  • String
  • Numerische Berechnungen
  • Fehlerbehandlung
  • Ein- und Ausgabe
  • Laufzeit-Typerkennung
  • Nationale Besonderheiten
  • Speicher-Verwaltung
  • Hilfsfunktionen
  • Standard Template Library (STL)
Was diese Funktionen und Klassen bewirken und wie man sie anwendet werde ich später in diesem Post noch erklären.
Wer meinen Artikel Einführung in C++ gelesen hat weiß, dass der Vorgänger von C++ die Programmiersprache C ist. Aus diesem Grund beinhaltet die C++ Standard-Library auch die C-Standard-Library, welche 18 Header-Dateien bereitstellt. Diese werde ich in der folgenden Tabelle vorstellen.

Header-Datei Erklärung
<cassert> Wenn der Ausdruck nicht wahr ist, bricht das Programm mit einer Fehlermeldung ab
<cctype> Klassifizierung von Zeichen
<cerrno> Error-Fehlernummern für Bibliotheksfunktionen
<climits> Minimaler und maximaler Wert von Ganzzahl-Typen
<cfloat> Minimaler und maximaler Wert von Fließkommazahl-Typen
<ciso646> Operatormakros
<clocale> Kulturelle Besonderheiten abbilden
<cmath> Mathematische Funktionen
<csetjmp> Ausführung von nicht lokalen goto-Anweisungen
<csignal> Aktion, die bei einem bestimmten Signal ausgelöst wird
<cstdarg> Variable Argumentenlisten für Funktionen mit einer variablen Anzahl von Parametern
<cstddef> Definition von einfachen Typen und Makros wie z.B. den NULL-Zeiger
<cstdio> Ein- und Ausgabe auf die Konsole oder in Dateien
<cstdlib> Allgemeine Funktionen, z.B. zur Erstellung von Zufallszahlen
<cstring> C-Funktionen für nullterminierte Zeichenfelder
<ctime> Zeit- und Datumsfunktionen
<cwchar> Umwandlung von Strings zu Zahlen für den Unicode-Zeichensatz
<cwctype Zeichenuntersuchung für den Unicode-Zeichensatz

Diese Headerdateien müssen mit der Anweisung #include eingebunden werden. Desweiteren nutzen alle Header den Namensraum std, den wir im ersten C++ Tutorial bereits kennengelernt haben.

Nun werde ich, wie versprochen, die einzelnen Bestandteile der C++ Standard-Bibliothek vorstellen, die es neben der C-Standard-Library noch gibt.

String
Header-Datei Erklärung
<string> Ermöglicht die Erstellung und Manipulation von Strings

Numerische Berechnungen
Header-Datei Erklärung
<complex> Komplexe Zahlen
<valarray> Gleitkommaberechnung mit großen Datenmengen, die auf eindimensionale Zahlenfeldern basieren
<limits> Größeninformationen, wie z.B der minimale Wert
<numeric> Funktionen zur Summenbildung, der Differenz und des Skalarprodukts

Fehlerbehandlung
Header-Datei Erklärung
<exception> Ausnahmebehandlung
<stdexept> Fehlerreports

Ein- und Ausgabe
Header-Datei Erklärung
<istream> Eingabe
<ostream> Ausgabe
<iostream> Ein- und Ausgabe mit cin, cout und cerr
<iomanip> Manipulatoren für Ein- und Ausgabe
<ios> Grundlage für die Ein- und Ausgabe
<iofwd> Grundlage für die Ein- und Ausgabe
<fstream> Zeichen aus Dateien lesen oder in Dateien schreiben
<streambuf> Ein- und Ausgabe auf gebufferten Streams
<sstream> Zeichenketten lesen und in String-Objekte schreiben

Laufzeit-Typerkennung
Header-Datei Erklärung
<typeinfo> Laufzeit-Typinformation als Text

Nationale Besonderheiten
Header-Datei Erklärung
<local> Kulturelle Unterschiede der Zeichen

Speicherverwaltung
Header-Datei Erklärung
<memory> Speicheranforderung und -freigabe
<new> Speicheranforderung

 Hilfsfunktionen
Header-Datei Erklärung
<utility> Fügt 2 Werte zu einem Paar zusammen. Elemente bestehen aus einem Wert und einem Schlüssel. Der Schlüssel dient der Identifizierung
<functional> Definiert STL-Funktionen, die helfen Objekte zu konstruieren

Donnerstag, 24. Mai 2012

Textformatierung in C++

In C++ gibt es die Möglichkeit seinen Text mit Hilfe von sogenannten Escape-Sequenzen zu formatieren. Escape-Sequenzen kennzeichnen sich dadurch, dass sie immer mit einem Backslash beginnen. In der folgenden Tabelle werden die wichtigsten von ihnen vorgestellt.

Escape-Sequenz Bedeutung
\\ fügt einen Backslash ein
\" fügt ein Anführungszeichen ein
\0 Nullzeichen (Ende einer Zeichenkette)
\n Zeilenumbruch (new line)
\t horizontaler Tabulator
\v vertikaler Tabulator
\r kehrt zum Zeilenanfang zurück (carriage return)
\b löscht das letzte Zeichen (backspace)
\a akustisches Signal


Die Einbindung von Escape-Sequenzen in einen Quelltext funktioniert sehr leicht, wie das nachfolgende Beispiel zeigt.

C++

   cout << "Textformatierung\nin \"C++\"\nist\nsehr\neinfach\n";

Java

   System.out.println("Textformatierung\nin \"Java\"\nist ebenfalls\neinfach\n";

Dienstag, 22. Mai 2012

Das erste Programm in C++

Zunächst wird eine geeignete Entwicklungsumgebung (IDE) benötigt, um C++ Programme zu programmieren. In diesem Tutorial werde ich Visual C++ 2010 Express von Microsoft verwenden, es gibt jedoch auch gute Alternativen, wie z.B Code::Blocks oder Dev-C++. Solltest du sich gegen Visual C++ 2010 Express entscheiden achte beim Download der IDE darauf, dass ein Compiler integriert ist, der im besten Fall den neusten Standard C++11 unterstützt. Anderenfalls kann der Quelltext nicht in eine ausführbare Datei (exe) umgewandelt werden. Nachdem die IDE heruntergeladen und installiert wurde, steht dem ersten Programm nichts mehr im Wege.

Jetzt geht es los

Erstelle nun ein neues Projekt und wähle "Win32-Konsolenanwendung" aus. Nun muss noch ein Name eingegeben und auf OK geklickt werden. Im nächsten Fenster muss auf "Weiter" geklickt werden und dann unter "Zusätzliche Optionen" einen Haken bei "Leeres Projekt" gesetzt werden. Nachdem das Projekt erstellt wurde fehlt noch die C++-Datei, in welcher der Code gespeichert wird. Klicke dazu auf "Neues Element hinzufügen" und wähle "C++-Datei" aus. Nachdem du auf "Hinzufügen" geklickt hast kannst du dich endlich dem wesentlichen Teil widmen, dem Code.

Der Code

In der Regel beginnt jeder Programmierer mit einem sogenannten "Hello World"-Programm, da wir dieses Ritual natürlich nicht brechen wollen, schreiben wir uns ebenfalls ein solches Programm. Zunächst werde ich hier den Code vorstellen und ihn danach erklären.

1  #include <iostream>
2  
3  using namespace std;
4  
5  int main()
6  {
7      cout << "Hello World!" << endl;
8      
9      return 0;
10 } 

Wie, nur 10 Zeilen? Ist das nicht ein bisschen wenig? Nein, dieses Programm weist bereits die grundlegende Struktur auf, die in fast jedem C++ Programm gleich ist. Aus diesem Grund wird es auch so gerne als erstes Beispiel in Tutorials genommen. Kommen wir nun zur Erklärung.

In der ersten Zeile wird die Headerdatei "iostream" durch die Präprozessor-Direktive "#include" eingebunden. Dadurch werden bestimmte Funktionen zur Verfügung gestellt, die den Input und Output, also die Ein- und Ausgabe von Text regeln. Präprozessor-Direktiven werden immer vor dem nachfolgenden Code durch den Compiler eingebunden. Es gibt neben "#include" noch viele weitere Präprozessor-Direktiven.

In der dritten Zeile wird der Standard-Namensbereich (std) eingebunden, somit können wir Funktionen oder Variablen, welche in diesem Namensbereich gespeichert sind einfacher verwenden. Anderenfalls müsste man vor jeder std-Funktion oder Variable "std::" schreiben.

Nun kommen wir zum "Herz" unseres kleinen Programms, der main-Funktion. Die main-Funktion ist Teil eines jeden C++ Programms, diese wird beim Programmstart immer als erstes aufgerufen, von ihr ausgehend wird also das komplette Programm gesteuert.
Der Code einer jeden Funktion steht innerhalb von geschweiften Klammern { }, diese signalisieren den Beginn und das Ende einer jeden Funktion.
Die main-Funktion gibt immer einen Wert vom Datentyp int zurück, wird der Wert Null zurückgegeben bedeutet dies, dass keine Fehler aufgetreten sind. Eine solche Rückgabe erfolgt in Zeile 9 durch das Schlüsselwort return.

In Zeile 7 kommt nun der interessante Teil des Programms, für diese Zeile haben wir sowohl den Header "iostream" als auch den Namensbereich "std" eingebunden. Mit Hilfe des Befehls "cout" ist es möglich Text auf den Bildschirm auszugeben. Unschwer zu erkennen ist, dass hier der Text "Hello World!" ausgegeben werden soll. Dieser Text wird mit Hilfe des Umleitungsoperators "<<" an "cout" übergeben und muss immer in Anführungszeichen stehen. Durch den Befehl "endl" wird ein Zeilenumbruch erzeugt, wodurch möglicher folgender Code in eine neue Zeile gerückt wird.

Was ebenfalls auffällt ist, dass jede Befehlszeile mit einem Semikolon endet. Dies ist notwendig, damit der Compiler erkennt, dass ein Befehl zu Ende ist. Präprozessor-Direktiven und Funktionsköpfe benötigen jedoch kein Semikolon.

Nachdem der Code vollständig eingetippt wurde muss er nur noch kompiliert werden. In Visual C++ 2010 Express muss dazu F5 gedrückt werden. Sollten ihr eine andere Entwicklungsumgebung nutzen, schaut bitte in der Anleitung nach, welche Taste für das Kompilieren verwendet wird. Nun sollte sich ein Konsolenfenster öffnen, in welchem die Worte "Hello World!" zu lesen sind.
Manchmal kommt es vor, dass sich das Konsolenfenster nach einem kurzen Moment wieder von alleine schließt. Tritt dieser Fehler auf, so könnt ihr ihn durch den Aufruf cin.get()  (vor return 0) beheben.

Sonntag, 20. Mai 2012

Operatoren in C++

Operator Bezeichnung Beschreibung Anwendung
+ positives Vorzeichen kann weggelassen werden, nur sinnvoll um explizit auf das Vorzeichen hinzuweisen int a = +1;
- negatives Vorzeichen kehrt das Vorzeichen um int a = -1;
int b = -a;
(b wird zu +1)
+ Addition addiert zwei Operanden miteinander int a;
int b;
int c = a + b;
- Subtraktion subtrahiert den rechten Operanden vom linken int a;
int b;
int c = a - b
* Multiplikation multipliziert zwei Operanden miteinander int a;
int b;
int c = a * b;
/ Division dividiert den linken Operanden durch den rechten, ein eventueller Rest fällt weg int a;
int b;
int c = a / b;
% Modulo gibt den Rest einer Division aus int a = 10;
int b = 3;
int c = a % b;
(c wird zu 1)
++ Inkrement (als Präfix-Inkrement oder Postfix-Inkrement anwendbar) erhöht den Wert des Operanden um 1 (Postfix-Inkremente haben eine höhere Priorität als Präfix-Inkremente) Postfix
int a = 1;
int b = a++;
Präfix
int a ;
int b = ++a;
(b wird zu a+1)
-- Dekrement (als Präfix-Dekrement oder Postfix-Dekrement anwendbar) verringert den Wert des Operanden um 1 (Postfix-Dekremente haben eine höhere Priorität als Präfix-Dekremente) Postfix
int a;
int b = a--;
Präfix
int a = 1;
int b = --a;
(b wird zu a-1)
= Zuweisung weist dem linken Operanden den Wert des rechten zu int a = 1;
+=, -=, *=, /=, %=, &=, <<=, >>=, ^=, |= kombinierte Zuweisungsoperatoren der linke Operator wird sowohl als linker Operator für die Zuweisung als auch für den rechten Operator verwendet a += b
(bedeutet a = a + b)
== Gleichheit vergleicht zwei Operanden miteinander, wenn beide gleich sind wird der boolsche Wert true, anderenfalls false zurückgegeben a == b
!= Ungleichheit vergleicht zwei Operanden miteinander, wenn beide ungleich sind wird der boolsche Wert true, anderenfalls false zurückgegeben a != b
<= kleiner oder gleich vergleicht zwei Operanden miteinander, wenn der linke Operand kleiner oder gleich dem rechten ist wird der boolsche Wert true, anderenfalls false zurückgegeben a <= b
>= größer oder gleich vergleicht zwei Operanden miteinander, wenn der linke Operand größer oder gleich dem rechten ist wird der boolsche Wert true, anderenfalls false zurückgegeben a >= b
< kleiner vergleicht zwei Operanden miteinander, wenn der linke Operand kleiner als der rechte ist wird der boolsche Wert true, anderenfalls false zurückgegeben a < b
> größer vergleicht zwei Operanden miteinander, wenn der linke Operand größer als der rechte ist wird der boolsche Wert true, anderenfalls false zurückgegeben a > b
&& logisches UND Verknüpft die beiden Operanden und gibt true zurück, wenn beide Operanden den Wert true besitzen, ansonsten false a && b
|| logisches ODER Verknüpft die beiden Operanden und gibt true zurück, wenn einer der Operanden den Wert true besitzt, ansonsten false a || b
! logisches NICHT aus true wird false, aus false wird true !a
& bitweises UND Verknüpft jedes Bit beider Operanden int a;
int b;
int c = a & b;
| bitweises ODER Verknüpft jedes Bit beider Operanden int a;
int b;
int c = a | b;
^ bitweises exklusives ODER Verknüpft jedes Bit beider Operanden int a;
int b;
int c = a ^ b;
~ bitweises NICHT Negiert den Wert jedes Bits int a;
int b = ~a;
<< Linksverschiebung Verschiebt die Bits des linken Operanden um die durch den rechten Operanden angegebene Anzahl von Stellen nach links und füllt die Stellen rechts mit Nullen int a;
int b;
int c = a << b;
>> Rechtsverschiebung Verschiebt die Bits des linken Operanden um die durch den rechten Operanden angegebene Anzahl von Stellen nach rechts, die nach rechts verschobenen Bits fallen weg int a;
int b;
int c = a >> b;
* Zeigerdereferenzierung Verhindert das Zugreifen auf die Adresse eines Zeigers, sodass nur auf den Speicherbereich zugegriffen wird int a = 5;
int* b = &a;*b = c;
. Elementzugriff Greift auf einen Member eines Objekts zu objekt.member = a;
-> Zugriff auf Member über Zeiger Dereferenziert einen Zeiger auf ein Objekt, der durch den linken Operanden angegeben wird, und greift auf den durch den rechten Operanden angegebenen Member zu obj_zeiger->member = a;
:: Qualifizierung Qualifizierung einer Variable, Funktion oder Klasse mit ihrem übergeordneten Element (z.B. Namespace) string a;
std::cin >> a;
.* Zugriff auf und gleichzeitige Dereferenzierung eines Members Zugriff auf und gleichzeitige Dereferenzierung eines Zeiger-Members eines Objekts objekt.*z_member = a;
->* Zugriff auf und gleichzeitige Dereferenzierung eines Members über einen Zeiger Zugriff auf, und gleichzeitige Dereferenzierung eines Zeiger-Member eines Objekt-Zeigers obj_z->*z_member = a;
& Adressoperator ermittelt die Adresse eines Operanden int a = 1;
int* zeiger = &a;
sizeof Speichergröße ermittelt den Speicherbedarf eines Datentyps bzw. eines Operanden int a = sizeof(int);
int b = sizeof a;
new Objekterstellung erstellt ein Objekt vom angegebenen Typ und gibt einen Zeiger darauf zurück Object * p = new Object(parameter);
new[] Objekt-Array-Erstellung erstellt ein neues Objekt-Array Object * object_array = new Object[12];
delete Objektzerstörung zerstört das gewünschte Objekt Object * a = new Object;
delete a;
delete[] Objekt-Array-Zerstörung Zerstört die Objekte im gewünschten Array delete[] object_array;
() Funktionsaufruf ruft eine Funktion auf function()
, Komma Operator (Aufzählung) Dient als Trennzeichen bei Initialisierungen, etc. int a, b, c;
* Zeigerdeklaration erstellt einen Zeiger auf einen bestimmten Datentypen int* zeiger;
?: Bedingungsoperator Ersatz für ein if-else Konstrukt, wenn die Bedingung in der Klammer zutrifft wird der erste Operand ausgeführt, wenn nicht der zweite int a = (b>c) ? a : b;
[] Indizierung greift auf ein bestimmtes Element im Array zu array[a] = 1;
(typ) Typumwandlung: explizites Casting in C Wandelt den Datentyp einer Variable in den gewünschten Datentyp um. Sollte jedoch in C++ so nicht verwendet werden, da Fehler auftreten können float a = 1.2;
int b = (int)a;
static_cast Typumwandlung in C++ Wandelt den Datentyp einer Variable in den gewünschten Datentyp um float a = 1.2;
int b = static_cast<int>(a);
const_cast const_cast Operator Entfernen der Konstanz von einem Datentyp, sodass ein Schreibzugriff ermöglicht wird const int a = 10;
const int b = const_cast(&a);
reinterpret_cast reinterpret_cast Operator sehr mächtiger Cast, mit dem man z.B. Zeiger in Datentypen umwandeln kann int a = 1;
float *b = reinterpret_cast<float*>(a);
typeid typeid Operator liefert während der Laufzeit Informationen über eine Variable, eine Referenz, einen (dereferenzierten) Zeiger oder eine Klasse #include <typeinfo>
class Base {/*...*/};
class Derived : public Base {/*...*/}
Derived dobj;
Base &bref = dobj;
if (typeid(bref) == typeid(Derived))
{
std::cout << "bref referenziert Derived" << std::endl;
}

Donnerstag, 17. Mai 2012

Datentypen in C++

Datentyp Speicherbedarf Beschreibung Wertebereich
int 4 Byte Ganzzahl (positiver und negativer Wertebereich) -2147483648 bis +2147483647
signed int 4 Byte Ganzzahl (positiver und negativer Wertebereich) -2147483648 bis +2147483647
unsigned int 4 Byte Ganzzahl (positiver Wertebereich) 0 bis +4294967295
short 2 Byte Ganzzahl (positiver und negativer, kleiner Wertebereich) -32768 bis +32767
signed short 2 Byte Ganzzahl (positiver und negativer, kleiner Wertebereich) -32768 bis +32767
unsigned short 2 Byte Ganzzahl (nur positver, kleiner Wertebereich) 0 bis +65536
long 4 Byte Ganzzahl (positiver und negativer, großer Wertebereich) -2147483648 bis +2147483647
signed long 4 Byte Ganzzahl (positiver und negativer, großer Wertebereich) -2147483648 bis +2147483647
unsigned long 4 Byte Ganzzahl (nur positver, großer Wertebereich) 0 bis +4294967295
float 4 Byte Gleitkommazahl (positiver und negativer Wertebereich, auf 7-8 Stellen genau) -3,4x1038 bis +3,4x1038
double 8 Byte Gleitkommazahl (positiver und negativer Wertebereich, auf 15-16 Stellen genau) -1,7x10308 bis +1,7x10308
long double 10 Byte Gleitkommazahl (positiver und negativer Wertebereich, auf 19-20 Stellen genau) -1,1x104932 bis +1,1x104932
char 1 Byte Buchstabe (positiver und negativer ASCII-Wert) -127 bis +127 (ASCII)
signed char 1 Byte Buchstabe (positiver und negativer ASCII-Wert) -127 bis +127 (ASCII)
unsigned char 1 Byte Buchstabe (positiver ASCII-Wert) 0 bis +255 (ASCII)
bool 1 Byte gibt true oder false zurück true (wahr) oder false (falsch)

Dienstag, 15. Mai 2012

Einführung in C++

C++ ist eine sogenannte "höhere Programmiersprache", welche 1979 von Bjarne Stroustrup entwickelt wurde. Der Vorgänger von C++ ist C; einer der größten Unterschiede zwischen beiden Sprachen ist, dass C++ objektorientierte Programmierung unterstützt. Demnach ist jedes C-Programm auch gleichzeitig mit C++ kompatibel.

C++ wurde ab 1979 immer weiterentwickelt und verbessert. Der aktuellste Standard ist C++11, welcher am 11.10.2011 veröffentlicht wurde. Aufgrung der Standardisierung der Sprache durch das American National Standards Institute (ANSI) ist die Portierung (z.B. von Windows auf Linux) von in C++ erstellten Programmen sehr viel einfacher, da sich die Compiler-Hersteller, wie beispielsweise Microsoft oder MinGW, an den ANSI-Standard halten müssen.

Ein weiterer Pluspunkt für C++ ist die Systemnähe, das bedeutet, dass C++ dem Programmierer einen großen Handlungsspielraum lässt. So kann man beispielsweise sogenannte Zeiger verwenden, welche dem Entwickler eine Menge Vorteile bringen (Mit ihnen ist es zum Beispiel möglich selber Speicher zu reservieren).

Außerdem ist C++ case-sensitive was bedeutet, dass auf Groß- und Kleinschreibung geachtet wird. Erstellt man beispielsweise die Funktion "Test()" kann man sie nicht mit "test()" oder "TEST()" aufrufen.

Dies war es auch schon zur Einführung in C++. Ich hoffe, ich konnte Ihnen die Sprache ein wenig näher bringen. Sollte es noch Fragen geben können Sie mir gerne eine E-Mail schreiben oder einen Kommentar posten.