C++-Programmierung/ Einführung zum Buch
Zielgruppe:
Alle die das Buch lesen wollen und/oder sich für Hintergrundinformationen über C++ interessieren.
Lernziel:
Das Lesen dieses Buches.
Wie lese ich dieses Buch? [Bearbeiten]
Egal, ob Sie schon C++ können, eine andere Programmiersprache beherrschen oder kompletter Programmieranfänger sind: Wenn Sie in C++ programmieren wollen, werden Sie mit diesem Buch etwas anfangen können. Es steht Ihnen frei, das Buch ganz normal von Anfang an zu lesen oder sich die Kapitel herauszupicken, die Sie interessieren. Allerdings sollten Sie beim Herauspicken darauf achten, dass Sie den Stoff der vorherigen Kapitel beherrschen, andernfalls werden Sie wahrscheinlich Schwierigkeiten haben, das Kapitel Ihres Interesses zu verstehen.
Dieses Buch gliedert sich in verschiedene Abschnitte, von denen jeder einen eigenen Schwierigkeitsgrad und entsprechend eine eigene Zielgruppe hat. Die meisten Abschnitte vermitteln Wissen über C++ oder sollen die praktische Anwendung zeigen, um das bisherige Wissen zu festigen. Andere weisen auf Besonderheiten und „Stolpersteine“ hin oder vermitteln Hintergrundwissen.
- Jeder Abschnitt enthält eine Anzahl von Kapiteln, welche einen komplexeren Zusammenhang erklären. Die Abschnitte sind in sich abgeschlossen, setzen aber voraus, dass Sie den Stoff der vorherigen Abschnitte verstanden haben.
- Am Ende jedes Kapitels stehen meistens ein paar Fragen und/oder Aufgaben, die Ihnen helfen sollen zu überprüfen, ob Sie verstanden haben, was im entsprechenden Kapitel stand. Die Antworten auf die Fragen lassen sich einfach ausklappen, für die Aufgaben gibt es je einen Verweis auf eine Seite mit einer Musterlösung. Diese Musterlösung ist natürlich nicht die einzige Variante, um die Aufgabe korrekt zu erfüllen, sie dient lediglich als Beispiel, wie Sie es machen könnten. Zu beachten ist allerdings, dass das im Kapitel erworbene Wissen genutzt werden sollte, um die Aufgabe zu erfüllen.
- Jeder Abschnitt schließt mit einer Zusammenfassung ab, die das vermittelte Wissen kurz und bündig auf den Punkt bringt. Wer schnell etwas nachschlagen will, kann sich in der Regel an die Zusammenfassungen halten. Auf Abweichungen von dieser Regel wird explizit hingewiesen.
„Für Programmieranfänger“ ist der nächste Abschnitt. Wenn Sie schon in einer anderen Programmiersprache Programme geschrieben haben, können Sie diesen Abschnitt getrost überspringen. Es geht nicht speziell um C++, sondern eher darum, zu begreifen, was überhaupt eine Programmiersprache ist und wie man dem Rechner sagen kann, was er zu tun hat.
Wenn Sie eine andere Programmiersprache schon sehr sicher beherrschen, reicht es wahrscheinlich, wenn Sie von den nächsten Abschnitten nur die Zusammenfassungen lesen, aber es kann Ihnen in keinem Fall schaden, auch die kompletten Abschnitte zu lesen. Die Zusammenfassungen sollen Ihnen zwar einen groben Überblick geben, was in diesem Abschnitt vermittelt wurde, aber es ist nun einmal nicht möglich, zur gleichen Zeit kurz und vollständig zu sein.
Sofern Sie C++ schon sicher beherrschen, können Sie sich einfach ein Kapitel aussuchen, es lesen und wenn nötig, in einem früheren Kapitel nachschlagen. Umsteigern ist dieses Verfahren hingegen nicht zu empfehlen, weil C++ eben doch etwas anderes ist als die meisten anderen Programmiersprachen.
In jedem Fall hoffen wir, dass dieses Buch Ihnen hilft, Programmieren zu lernen und Ihre Fähigkeiten zu verbessern, aber vergessen Sie niemals, dass ein Buch nur eine Stütze sein kann. Programmieren lernt man durchs Programmieren!
Es war einmal… [Bearbeiten]
C++ wurde von Bjarne Stroustrup ab 1979 in den AT&T Bell Laboratories entwickelt. Ausgangspunkt waren Untersuchungen des UNIX-Betriebssystemkerns in Bezug auf verteiltes Rechnen.
Auf die Idee für eine neue Programmiersprache war Stroustrup schon durch Erfahrungen mit der Programmiersprache Simula im Rahmen seiner Doktorarbeit an der Cambridge University gekommen. Simula erschien zwar geeignet für den Einsatz in großen Software-Projekten, die Struktur der Sprache erschwerte aber die für viele praktische Anwendungen erforderliche Erzeugung hocheffizienter Programme. Demgegenüber ließen sich effiziente Programme zwar mit der Sprache BCPL schreiben, für große Projekte war BCPL aber wiederum ungeeignet.
Mit den Erfahrungen aus seiner Doktorarbeit erweiterte Stroustrup nun die Programmiersprache C um ein Klassenkonzept, für das die Sprache Simula-67 das primäre Vorbild war. Die Wahl fiel auf die Programmiersprache C, eine Mehrzwecksprache, die schnellen Code produzierte und einfach auf andere Plattformen zu portieren war. Als dem Betriebssystem UNIX beiliegende Sprache hatte C außerdem eine nicht unerhebliche Verbreitung. Zunächst fügte er der Sprache Klassen (mit Datenkapselung) hinzu, dann abgeleitete Klassen, ein strengeres Typsystem, Inline-Funktionen und Standard-Argumente.
Während Stroustrup „C with Classes“ („C mit Klassen“) entwickelte (woraus später C++ wurde), schrieb er auch cfront, einen Compiler, der aus C with Classes zunächst C-Code als Zwischenresultat erzeugte. Die erste kommerzielle Version von cfront erschien im Oktober 1985.
1983 wurde C with Classes in C++ umbenannt. Erweiterungen darin waren: virtuelle Funktionen, Überladen von Funktionsnamen und Operatoren, Referenzen, Konstanten, änderbare Freispeicherverwaltung und eine verbesserte Typüberprüfung. Die Möglichkeit von Kommentaren, die an das Zeilenende gebunden sind, wurde wieder aus BCPL übernommen (//
).
1985 erschien die erste Version von C++, die eine wichtige Referenzversion darstellte, da die Sprache damals noch nicht standardisiert war. 1989 erschien die Version 2.0 von C++. Neu darin waren Mehrfachvererbung, abstrakte Klassen, statische Elementfunktionen, konstante Elementfunktionen und die Erweiterung des Schutzmodells um protected. 1990 erschien das Buch „The Annotated C++ Reference Manual“, das als Grundlage für den darauffolgenden Standardisierungsprozess diente.
Relativ spät wurden der Sprache Templates, Ausnahmen, Namensräume, neuartige Typumwandlungen und boolesche Typen hinzugefügt.
Im Zuge der Weiterentwicklung der Sprache C++ entstand auch eine gegenüber C erweiterte Standardbibliothek. Erste Ergänzung war die Stream-I/O-Bibliothek, die Ersatz für traditionelle C-Funktionen wie zum Beispiel printf()
und scanf()
bietet. Eine der wesentlichen Erweiterungen der Standardbibliothek kam später durch die Integration großer Teile der bei Hewlett-Packard entwickelten Standard Template Library (STL) hinzu.
Nach jahrelanger Arbeit wurde schließlich 1998 die endgültige Fassung der Sprache C++ (ISO/IEC 14882:1998) genormt. 2003 wurde ISO/IEC 14882:2003 verabschiedet, eine Nachbesserung der Norm von 1998.
Die vorhandenen Performance-Probleme der Sprache C++, die Rechenzeit und Speicherplatz betreffen, wurden auf hohem Niveau zusammen mit Lösungen im Technical Report ISO/IEC TR 18015:2006 diskutiert. Das Dokument ist zum Download von ISO freigegeben. (http://www.open-std.org/jtc1/sc22/wg21/docs/TR18015.pdf)
Die aktuelle Version wurde am 11. Oktober 2011 als ISO/IEC 14882:2011 von der ISO veröffentlicht. Inoffiziell wird der Name C++11 verwendet, der Entwurf lief lange Zeit unter der inoffiziellen Bezeichnung C++0x. Die Entwürfe des C++-Standards können unter http://www.open-std.org/JTC1/SC22/WG21/ heruntergeladen werden. Das Dokument http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3337.pdf stimmt mit C++11 bis auf minimale Änderungen überein.
Der offizielle Standard muss käuflich erworben werden, bei der ISO ist er für 238 Schweizer Franken zu haben, beim DIN zahlt man 659,60 €, beim ANSI steht der Preis bei 285 US-Dollar, bzw. 761 US-Dollar wenn man die britische Ausgabe kauft. Das INCITS verkauft ihn unter der Beizeichnung INCITS/ISO/IEC 14882-2012 für 30 US-Dollar. Es lohnt sich also die Preise der unterschiedlichen Herausgeber bzw. Anbieter zu vergleichen.
Der Name „C++“
[Bearbeiten]Der Name ist eine Wortschöpfung von Rick Mascitti und wurde zum ersten Mal im Dezember 1983 benutzt. Der Name kommt von der Verbindung der Vorgängersprache C und dem Inkrement-Operator „++“, der den Wert einer Variable um eins erhöht.
Weiterentwicklung der Programmiersprache C++
[Bearbeiten]Um mit den aktuellen Entwicklungen der sich schnell verändernden Computer-Technik Schritt zu halten, aber auch zur Ausbesserung bekannter Schwächen, erarbeitet das C++-Standardisierungskomitee derzeit die nächste größere Revision von C++, die inoffiziell mit C++16 abgekürzt wird, worin die Ziffernfolge eine grobe Einschätzung des möglichen Erscheinungstermins andeuten soll. Derzeit ist 2016 als Termin im Gespräch, doch ist eine Verschiebung um wenige Jahre durchaus nicht unwahrscheinlich.
Erweiterung der Programmbibliothek
[Bearbeiten]Im April 2006 gab das C++-Standardisierungskomitee den so genannten ersten technischen Report (TR1) heraus, eine nichtnormative Ergänzung zur damals gültigen, 1998 definierten Bibliothek, mit der Erweiterungsvorschläge vor einer möglichen Übernahme in die C++-Standardbibliothek auf ihre Praxistauglichkeit hin untersucht werden sollen.
Enthalten sind im TR1 u.a. reguläre Ausdrücke, verschiedene intelligente Zeiger, ungeordnete assoziative Container, eine Zufallszahlenbibliothek, Hilfsmittel für die C++-Metaprogrammierung, Tupel sowie numerische und mathematische Bibliotheken. Die meisten dieser Erweiterungen stammen aus der Boost-Bibliothek, woraus sie mit minimalen Änderungen übernommen wurden. Außerdem sind viele Bibliothekserweiterungen der 1999 überarbeiteten Programmiersprache C (C99) in einer an C++ angepassten Form enthalten.
Mit Ausnahme der numerischen und mathematischen Bibliotheken wurde die Übernahme aller TR1-Erweiterungen in die aktuelle Sprachnorm C++11 vom C++-Standardisierungskomitee beschlossen.
In Vorbereitung auf den nächsten Standard wird die Erweiterung im Bereich der Netzwerke, des Dateisystems und der Verwendung von XML und möglicherweise auch HTML diskutiert.
Compiler [Bearbeiten]
Was passiert beim Übersetzen
[Bearbeiten]Als Übersetzen oder compilieren bezeichnet man den Vorgang, den als Text geschriebenen Quellcode (engl. source code) in eine Sprache zu überführen, die der Computer versteht. Also in Einsen und Nullen. Dies passiert bei C++ in drei Schritten. Um diese zu verstehen, ist zunächst ein Verständnis für die Dateistruktur von C++-Quellcode erforderlich.
Grob gesagt gibt es zwei Kategorien von Quellcode-Dateien in C++: Quelldateien und Headerdateien. Quelldateien sollten den Code enthalten, der die eigentlichen Anweisungen enthält und somit das Verhalten des Programms beschreibt. Headerdateien sollten Daten enthalten, welche die Struktur des Programms beschreiben. Um zu definieren, wie das Verhalten mit der Struktur verknüpft ist, bindet typischerweise jede Quelldatei mehrere Headerdateien ein.
Dies ist wie gesagt eine sehr grobe Beschreibung des Aufbaus, die jedoch beim Grundverständnis helfen kann. Damit können wir nun zum Übersetzungsprozess kommen.
Als erstes ist der Präprozessor (engl. Preprocessor) dran, dessen Hauptaufgabe es ist, alle benötigten Headerdateien in die Quellcodedateien zu kopieren. Auf diesen vom Präprozessor zusammengefügten Quelldateien arbeitet nun der Compiler. Er erzeugt aus jeder dieser Dateien eine sogenannte Objektdatei (engl. object file), die nicht mehr C++, sondern Maschinensprache enthält. Um aus diesen Objektdateien ein ausführbares Programm zu machen, müssen die Objektdateien schließlich vom Linker gebunden werden. (denglisch linken, engl. linking)
Das Ganze noch mal in Stichpunkten:
- Präprozessor: in jede Quelle alle nötigen Headern einfügen
- Compiler: jede vom Präprozessor erstellte C++-Quelle in Maschinensprache übersetzen
- Linker: in Maschinensprache übersetzte Dateien zu einem einzigen Programm zusammensetzen
Werkzeuge
[Bearbeiten]Wie es immer mit Software ist, stellt sich am Anfang die Frage, auf welcher Plattform bzw. welchem Betriebssystem man arbeitet. Ich (Prog) verwende seit vielen Jahren Linux und bin gerade beim Thema Softwareentwicklung sehr zufrieden damit. Auch Mac OS X soll sich sehr gut zum Programmieren eignen, wobei ich hier nur Erfahrungsberichte kenne. Microsoft Windows hingegen macht mir das Leben jedes Mal zur Hölle, wenn ich gezwungen bin dies zu verwenden. Immerhin ist die Situation in den letzten Jahren etwas besser geworden, da Microsoft sich beispielsweise um Tools zur automatisierten Einrichtung von Drittbibliotheken bemüht und generische Werkzeuge wie Git und die dazugehörigen Plattformen wie github.com oder gitlab.com unter allen Betriebssystemen funktionieren.
Es gibt natürlich eine ganze Reihe von C++-Compilern, und wenn ich im Folgenden von Compilern spreche, meine ich auch Präprozessor und Linker, da diese drei Komponenten üblicherweise in einem Programm zusammengefasst sind. Die drei verbreitetsten Compiler sind GCC (OpenSource), clang (OpenSource) und Visual C++ (proprietär). Visual C++ darf in der kostenfreien Variante von Visual Studio nur für private Zwecke verwendet werden. GCC und clang dürfen auch kommerziell eingesetzt werden.
- clang (hohe Standardkonformität, gute Fehlermeldungen)
- Betriebssysteme: Linux, Mac OS X, Windows
- GCC (hohe Standardkonformität)
- Betriebssysteme: Linux, Mac OS X, Windows
- Visual C++ (schlechtere Standardkonformität und wesentlich langsamer als clang und GCC, holt aber langsam auf)
- Betriebssysteme: Windows
Ich entwickle gern »direkt von der Kommandozeile« aus, wobei ich für größere Projekte Boost.Build einsetze. Auf diese Weise hat man eine hohe Flexibilität und maximale Kontrolle über alle Vorgänge. Meine Entwicklungsumgebung besteht also im Wesentlichen aus einem Texteditor (ich verwende unter Linux Kate, den Standardeditor des KDE-Desktops, unter Windows Notepad++) und eine Kommandozeile, um den Compiler aufzurufen.
Die meisten Entwickler bevorzugen eine Integrierte Entwicklungsumgebung (kurz IDE) die ihnen den direkten Kontakt mit dem Compiler abnimmt. Speziell wenn Sie unter Windows arbeiten, ist eine IDE dringend zu empfehlen, weil die Kommandozeile von Windows eine absolute Katastrophe ist.
Im Folgenden eine kurze Liste der aus meiner Sicht wichtigsten IDEs:
- Visual Studio Code (nicht verwechseln mit Visual Studio!)
- Betriebssysteme: Linux, Mac OS X, Windows
- Compiler: GCC, clang, Visual C++
- QtCreator
- Betriebssysteme: Linux, Mac OS X, Windows
- Compiler: GCC, clang, Visual C++
- XCode (von Apple)
- Betriebssysteme: Mac OS X
- Compiler: clang
- Visual Studio (von Microsoft)
- Betriebssysteme: Windows
- Compiler: Visual C++, (clang – experimentell)
Ich empfehle sehr den QtCreator als IDE, da er Betriebssystem- und Compiler-Übergreifend eingesetzt werden kann und obendrein vergleichsweise einfach in der Bedienung ist. Windows-Nutzer können eine Version herunterladen, in die der GCC für Windows bereits integriert ist. Alternativ kann auch erst Visual Studio installiert werden und anschließend der QtCreator, der dann den Microsoft Compiler verwendet.
Wenn Sie QtCreator einsetzen und als Compiler GCC (Voreinstellung) oder clang verwenden, dann müssen Sie eventuell explizit den C++-Standard (C++11, C++14, C++17 …) angeben. Da sich das Vorgehen hierzu ändert, verwenden Sie bitte eine Suchmaschine um herauszufinden, wie dies geht.
Unter Mac OS X verwenden die meisten Entwickler wohl XCode. Ich kenne dazu wieder nur Erfahrungsberichte, die sind allerdings in der Regel positiv.
Visual Studio Code verwende ich erst seit kurzem und bislang nicht für C++. Das Programm ist recht gut, eine Empfehlung kann ich aufgrund mangelnder eigener Erfahrung zur Zeit noch nicht aussprechen.
Visual Studio ist als IDE ganz gut, auch wenn es einige Macken hat, die sich auch gerne mal von PC zu PC unterscheiden können. Es ist sehr umfangreich, was allerdings auch zu einer recht komplexen Bedienung führt. Gerade für Anfänger kann es daher schwierig zu bedienen sein. Der größte Nachteil von Visual Studio ist allerdings die Festlegung auf Visual C++ als Compiler. Fakt ist, dass Visual Studio in der Industrie weit verbreitet ist.
Falls Sie Windows verwenden und bislang keine Erfahrung mit Programmierung haben, kann ich empfehlen, VirtualBox zu installieren und darin dann ein Linux (z. B. Kubuntu Long Term Support) laufen zu lassen. Das erlaubt es clang oder GCC ohne die massiven Schwierigkeiten zu verwenden, die man unter Windows mit diesen Compiler und auch in einigen Aspekten der eigentlichen Entwicklung hat.
Was genau VirtualBox ist und wie es funktioniert finden Sie mit einem (Video)-Tutorial Ihrer Wahl sicher schnell heraus. Die Felsen die Windows einem in den Weg legt, kompensieren nach meiner Erfahrung recht schnell den Aufwand, ein paar Sachen rund um die Nutzung von Linux lernen zu müssen. Allein die Kommandozeile von Linux ist im Vergleich zur "CMD" diesen Aufwand wert.
GCC & clang
[Bearbeiten]GCC steht für Gnu Compiler Collection und ist eine Sammlung von freien Compilern, darunter auch den C++-Compiler g++
. Der C++-Compiler clang++
gehört hingegen zum LLVM-Projekt. Achten Sie bei clang
auf das ++
, da clang
ohne ++
(ein C-Compiler) zwar ebenfalls C++ übersetzen kann, wenn man den C++-Standard angibt, aber dann meist beim Linken Probleme macht.
Sowohl GCC als auch LLVM stellen auch Compiler für viele andere Sprachen bereit. Beide sind weit verbreitet. GCC ist deutlich älter, während sich LLVM in den letzten Jahren immer weiter steigender Beliebtheit erfreut und die Compiler der GCC inzwischen in einigen Punkten überholt hat. Beide sind für viele Betriebssysteme und Prozessorarchitekturen verfügbar, wobei GCC hier noch leicht die Nase vorn hat. Die wichtigsten Kommandozeilen-Parameter sind bei beiden identisch, so dass es egal ist, welchen Sie primär verwenden. Sie können jederzeit mal eben auch mit dem jeweils anderen übersetzen, um »eine zweite Meinung« zu Ihrem Code zu bekommen, falls Probleme auftreten.
Der größte Unterschied zwischen den beiden, ist ein Politischer. Die GCC steht unter der GNU General Public License (GNU GPL), LLVM-Projekte stehen unter BSD- und MIT-ähnlichen Lizenzen. Das heißt, beide sind Freie Software und jeder ist berechtigt, den Quellcode einzusehen, zu verändern und natürlich auch beliebigen Quellcode mit den Compilern zu übersetzen. Sie dürfen also sowohl private Projekte, als auch kommerzielle Projekte ohne Einschränkung mit diesen Compilern übersetzen. Bei Visual Studio benötigen Sie hingegen eine gekaufte Lizenz, wenn Sie kommerzielle Produkte damit übersetzen wollen.
Allerdings müssen Änderungen am Quellcode von GCC wiederum unter die GNU GPL gestellt werden. Änderungen an LLVM-Projekten dürfen unter eine beliebige Lizenz gestellt werden. Für Sie als Entwickler, der die Compiler nur verwendet, und keine Änderungen daran vornimmt, spielt das keine Rolle, aber für Firmen, die den Compiler in ihre Produkte integrieren wollen. Sie sind im Falle von GCC gezwungen, mit der Community zusammenzuarbeiten, und können ihre eigenen Änderungen an GCC nicht vor der Welt verstecken, sondern müssen sie wieder freigeben.
Auf fast allen GNU/Linux ist GCC üblicherweise der Standardcompiler. Unter Mac OS X und FreeBSD (ein weiteres freies Unixoides Betriebssystem) ist clang inzwischen der Standardcompiler. Unter Windows gibt es in dem Sinne keinen Compiler, weil es unter Windows nie üblich war, dass Benutzer selbst Programme übersetzten. Dies liegt vor allem daran, dass Windows, verglichen mit dem Stammbaum der Unixoiden Systeme (BSD, Mac OS X, Linux & viele weitere) ein sehr junges Betriebssystem ist und zum Zeitpunkt der Einführung Software schon nicht mehr im Quellcode ausgeliefert wurde.
Aber jetzt ist Schluss mit der Theorie. Schauen wir uns erst einmal an, wie g++ benutzt wird. Im folgenden wird angenommen, dass eine Datei mit dem Namen prog.cpp vorhanden ist. In dieser Datei könnte zum Beispiel folgendes stehen:
Dieses Programm müssen Sie noch nicht verstehen, im Kapitel Hallo, du schöne Welt! wird es erklärt. Nun geben Sie auf der Kommandozeile (GNU/Linux: Shell, Windows: Eingabeaufforderung) folgendes ein:
g++ prog.cpp
g++ ist der Name des Programms, welches aufgerufen wird, also der Compiler g++. prog.cpp ist der Name der Datei, die kompiliert werden soll. Wenn Sie diesen Befehl ausführen, sehen Sie entweder Fehlermeldungen auf dem Bildschirm, oder aber Sie bekommen eine Datei mit dem Namen a.out. Manchmal bekommen Sie auch sogenannte „warnings“, also Warnungen. Bei diesen Warnungen wird der Code zwar kompiliert, aber Sie sollten versuchen, warnungsfreie Programme zu schreiben. Ein Beispiel für eine Warnung könnte z.B. sein:
g++ prog.cpp prog.cpp: In function `int main()': prog.cpp:17: warning: comparison between signed and unsigned integer expressions
In diesem Beispiel würde die Warnung bedeuten, dass wir eine Zahl ohne Vorzeichen (unsigned
) und eine mit Vorzeichen (signed
) vergleichen, und zwar innerhalb der Funktion `int main()
', genauer gesagt in Zeile 17. Was unsigned
und signed
ist, erfahren Sie im Kapitel Variablen, Konstanten und ihre Datentypen.
Es gibt auch einige, zum Teil hilfreiche Warnungen, die nicht angezeigt werden. Um diese zu sehen, müssen Sie die Option -Wall hinzufügen.
g++ -Wall prog.cpp
Um sich noch mehr Warnungen anzeigen zu lassen (-Wall zeigt auch nicht alle), können Sie auch noch -Wextra benutzen:
g++ -Wall -Wextra prog.cpp
Es wird empfohlen, diese Möglichkeit zu nutzen, vor allem wenn Sie auf der Suche nach Fehlern in Ihrem Programm sind.
Möchten Sie, dass das fertige Programm einen anderen Namen als a.out hat, so können Sie es natürlich jedesmal umbenennen. Dies ist aber umständlich und somit nicht zu empfehlen. Ein viel eleganterer Weg ist das Benutzen der Option -o.
g++ -o tollername prog.cpp
Der Dateiname nach der Option -o gibt an, in welche Datei das kompilierte Programm gespeichert werden soll. So erhalten Sie in diesem Beispiel die ausführbare Datei tollername.
Sollten Sie sich einmal nicht mehr erinnern, was eine bestimmte Funktion bewirkte oder wie sie hieß, so können Sie g++ einfach nur mit der Option --help aufrufen.
g++ --help
g++ gibt dann eine kurze Auflistung der Optionen aus. Wer gerne eine detailliertere Version hätte, kann unter GNU/Linux auch das Manualprogramm „man“ benutzen:
man g++
Ausführlichere und übersichtlichere Dokumentation, die nicht nur die Kommandozeilenoptionen vorstellt, sind im Dokumentationssystem info
zu finden, das auf vielen GNU/Linux- und BSD-Systemen installiert ist:
info g++
Für Fortgeschrittenere
[Bearbeiten]Jetzt sollten Sie erst einmal den Rest des Kapitels überspringen und mit einigen der folgenden Buchkapitel weitermachen, denn für den Rest ist ein wenig Vorwissen sehr hilfreich. Sobald Sie mit den ersten paar Kapiteln fertig sind, können Sie hierher zurückkehren und den Rest lesen.
Was passiert überhaupt, wenn Sie g++ aufrufen? Nun, als erstes wird der Code vom Präprozessor durchgeschaut und bearbeitet. (Natürlich bleibt Ihre Quelldatei, wie sie ist.) Dabei werden beispielsweise Makros ersetzt und Kommentare gelöscht. Dieser bearbeitete Code wird dann vom Compiler in die Assemblersprache übersetzt. Die Assemblersprache ist auch eine Programmiersprache, welche aber nur die Maschinensprache so darstellt, dass ein Mensch sie (leichter) lesen kann. Schließlich wird diese Assemblersprache von einem Assembler in Maschinencode umgewandelt. Zum Schluss wird noch der Linker aufgerufen, der die einzelnen Programmdateien und die benutzten Bibliotheken "verbindet".
Darum, dass dies alles in korrekter Reihenfolge richtig ausgeführt wird, brauchen Sie sich nicht zu kümmern; g++ erledigt das alles für Sie. Allerdings kann es manchmal nützlich sein, dass das Programm noch nicht gelinkt wird, etwa wenn Sie mehrere Quelldateien haben. Dann kann man einfach dem g++ die Option -c mitgeben.
g++ -c -o prog.o prog.cpp
Durch diesen Aufruf erhalten wir eine Datei prog.o, die zwar kompiliert und assembliert, aber noch nicht gelinkt ist. Deswegen wurde die Datei auch prog.o genannt, da ein kompiliertes und assembliertes, aber nicht gelinktes Programm als Objektdatei vorliegt. Objektdateien bekommen üblicherweise die Endung *.o. Ohne die -o Option hätten Sie möglicherweise eine Datei gleichen Namens erhalten, aber das ist nicht absolut sicher. Es ist besser den Namen der Ausgabedatei immer mit anzugeben; so können Sie sicher sein, dass die ausgegeben Dateien auch wirklich den von Ihnen erwarteten Namen haben.
Bei g++ gibt es Unmengen von Optionen, mit denen Sie fast alles kontrollieren können. So gibt es natürlich auch eine Option, durch die kompiliert, aber nicht assembliert wird. Diese Option heißt -S.
g++ -S prog.cpp
Diese Option wird allerdings fast nie benötigt, es sei denn Sie interessieren sich dafür, wie Ihr Compiler Ihren Code in Assembler umsetzt. Die Option -E ist schon nützlicher. Mit ihr wird nur der Präprozessor ausgeführt:
g++ -E prog.cpp
So können Sie z. B. sehen, ob mit den Makros alles ordnungsgemäß geklappt hat oder die Headerdateien eventuell in einer von Ihnen unerwarteten Reihenfolge inkludiert wurden. Eine Warnung sollen Sie hier aber mit auf den Weg bekommen. Wenn der Präprozessor durchgelaufen ist, stehen auch alle mittels #include
eingebundenen Headerdateien der Standardbibliothek mit im Quelltext, die Ausgabe kann also ziemlich lang werden. Allein das Einbinden von iostream
produziert bei g++ 4.6.3 knapp 19 000 Zeilen Code. Welche Zeit es in Anspruch nimmt, den Quellcode nach dem Übersetzen in Assembler (Option -S) zu lesen, dürfte damit geklärt sein.
Sie sollten auch die Option -ansi kennen. Da g++ einige C++ Erweiterungen beinhaltet, die nicht im C++-Standard definiert sind oder sogar mit ihm im Konflikt stehen, ist es nützlich, diese Option zu verwenden, wenn Sie ausschließlich mit Standard-C++ arbeiten wollen. In der Regel ist das zu empfehlen, da sich solcher Code viel leichter auf andere Compiler portieren lässt. Im Idealfall müssten Sie gar keine Änderungen mehr vornehmen. Da aber weder g++ noch irgendein anderer Compiler absolut standardkonform sind, ist das selten der Fall. Ein Negativbeispiel für standardkonforme Compiler kommt immer mal wieder aus Redmond. Die dort ansässige Firma produziert in der Regel Produkte, die zu ihren selbstdefinierten Standards in einer bestimmten Version (also nicht mal untereinander) kompatibel sind. Beschweren Sie sich bitte nicht, wenn einige Programmbeispiele mit deren Compiler nicht kompiliert werden. Der Fairness halber soll trotzdem angemerkt werden, dass langsam aber doch sicher Besserung in Sicht ist.
g++ -ansi -o prog prog.cpp
Dies bewirkt, dass diese nicht standardkonformen Erweiterungen des g++-Compilers abgeschaltet werden. Solcher Code ist in der Regel die bessere Wahl, da er auch in Zukunft, wenn es neue Compiler oder Compiler-Versionen geben wird, noch mit ihnen übersetzt werden kann.
Möchten Sie eine Warnung erhalten, wenn nicht standardkonforme Erweiterungen von g++ verwendet werden, dann nutzen Sie die Option -pedantic:
g++ -pedantic -o prog prog.cpp
Um die Optimierung zuzuschalten, nutzen Sie die Option -Ox, wobei das x für eine Zahl von 1 bis 3 steht. 3 bedeutet stärkste Optimierung.
g++ -O3 -o prog prog.cpp
Programme, die mit Optimierung übersetzt wurden, sind kleiner und laufen schneller. Der Nachteil besteht in der höheren Zeit, welche für die Übersetzung an sich benötigt wird. Daher sollten Sie die Optimierung nur zuschalten, wenn Sie eine Programmversion erstellen, die Sie auch benutzen möchten. Beim Austesten während der Entwicklung ist es besser, ohne Optimierung zu arbeiten, da häufig kompiliert wird. Eine lange Wartezeit, nur um dann einen Programmdurchlauf zu machen, ist schlicht nervtötend.
Externe Bibliotheken benutzen
[Bearbeiten]Die C++-Standardbibliothek ist zwar eine wohl durchdachte und vielfach bewährte Bibliothek für Ihre Programme; allerdings ist sie kein Wundermittel für alle Probleme. Um auch andere Bibliotheken mit einem Programm nutzen zu können, bietet der Compiler spezielle Optionen an.
Headerdateien finden
[Bearbeiten]Damit die Quelldateien, die die externe Bibliothek benutzen, übersetzt werden können, muss der Präprozessor die Headerdateien finden. Mit dem Befehl cpp -v werden die Standardverzeichnisse ausgegeben, in denen nach Headerdateien gesucht wird. Befinden sich die Dateien der Bibliothek in einem anderen Verzeichnis, muss dieses mit der Option -I angegeben werden. Die imaginäre Bibliothek libfoo bietet z.B. eine Headerdatei foo.h im Verzeichnis /usr/include/libfoo an. Folgender Befehl wäre damit nötig:
g++ -I/usr/include/libfoo -o prog prog.cpp
Die Quelldatei kann die Headerdatei nun wie jede andere Datei der Standardbibliothek einbinden:
Mit Bibliothek linken
[Bearbeiten]Es reicht aber noch nicht aus, dass die Headerdateien eingebunden werden können. Der Linker muss den compilierten Code der Quelldateien zusammen mit der Bibliothek linken. Dafür bietet der Compiler die Option -l an, die den Namen der Bibliothek benötigt. Die Zeichenkette lib kann weggelassen werden, aus libfoo wird also einfach nur foo:
g++ -I/usr/include/libfoo -lfoo -o prog prog.cpp
Makefiles
[Bearbeiten]Was ist make?
[Bearbeiten]Bei make handelt es sich um ein Werkzeug, mit dem man die Abhängigkeiten eines Build Prozesses auflösen kann. Dieses Stück Software ist schon sehr alt und in unterschiedlichsten Implementierungen verfügbar, die verbreitesten sind wohl GNU make und BSD make. Leider sind die verschiedenen Varianten untereinander nicht ganz kompatibel.
Makefiles per Hand erstellen
[Bearbeiten]make kann sehr viel mehr, als hier beschrieben werden könnte, es folgt daher lediglich eine kurze Erläuterung, um was es eigentlich geht. In einem Makefile lassen sich Regeln beschreiben, wie bestimmte "Targets" (Ziele) erstellt werden können. Diese können von anderen Zielen oder Dateien abhängen.
Beispielsweise erstellt man eine Objektdatei aus einer Sourcedatei, indem man sie kompiliert. Dazu muss aber natürlich die Sourcedatei vorhanden sein. Eine solche Regel könnte zum Beispiel so aussehen:
hello.o: hello.c
$(CC) -c $(CFLAGS) hello.c
Die erste Zeile besagt, dass zum Erstellen von hello.o
die Datei hello.c
benötigt wird. Die Zweite sagt aus, wie das Erstellen von hello.o
zu bewerkstelligen ist. Variablen werden mit $
eingeleitet. So beinhaltet zum Beispiel $(CC)
in der Regel den Namen des C Compilers.
Derartige Regeln kann man auch mit Wildcards versehen, so kann man eine Regel erstellen, mit der man ausdrücken kann, wie generell aus einer *.c eine *.o Datei zu erstellen ist:
%.o: %.c
$(CC) -c $(CFLAGS) $<
Dabei steht die spezielle Variable $<
für den Namen der tatsächlichen Source Datei, wie zum Beispiel hello.c
.
Beispiel:
CC = gcc
OBJECTS = cbg.o
LIBS = -lcurl
CFLAGS = -Wall -O2
BINDIR = $(DESTDIR)/usr/bin
NAME = cbg
cbg: $(OBJECTS)
$(CC) -o $(NAME) $(OBJECTS) $(LIBS)
%.o: %.c
$(CC) -c $(CFLAGS) $<
install:
install --mode=755 $(NAME) $(BINDIR)/
clean:
rm *.o $(NAME)
uninstall:
rm $(BINDIR)/$(NAME)
Damit ist es nun möglich, die einzelnen Ziele zu erstellen:
make install make clean make uninstall
Wird kein Ziel angegeben, so wird das erste Ziel erstellt, in obigen Beispiel also cbg.
Automake
[Bearbeiten]Boost Build
[Bearbeiten]Eine sehr gute Alternative zu Makefiles. http://www.boost.org/doc/tools/build/index.html
GUIs und C++ [Bearbeiten]
Was ist ein GUI?
[Bearbeiten]GUI kommt vom englischen „Graphical User Interface“, was soviel heißt wie „Graphische Benutzerschnittstelle“. Grob gesagt ist es das, was der Benutzer von den meisten Programmen zu sehen bekommt. Also: Die Menüleiste, Textfelder, Buttons u.s.w.
Weiterhin gibt es natürlich noch das „Command Line Interface“ (kurz CLI, zu Deutsch "Kommandozeilenschnittstelle"), das vielen als Fenster mit schwarzem Hintergrund und mit weißer Schrift bekannt ist.
C++ und GUIs
[Bearbeiten]C++ bringt von Haus aus kein GUI mit, da es als hardwarenahe plattformunabhängige Sprache erstellt wurde und GUIs stark vom verwendeten Betriebssystem abhängen. Dieser „Mangel“ könnte einige Neueinsteiger vielleicht auf den ersten Blick abschrecken, da sie sich einen Einstieg in C++ oder gar in die gesamte Programmierung mit vielen Buttons, Textfeldern, Statusanzeigen und Menüeintragen erhofft haben.
Wer jedoch einmal damit angefangen hat, kleinere Programme zu schreiben, wird schnell merken, dass der Reiz hier nicht von bunten Farben und blinkenden Symbolen ausgeht. Ganz im Gegenteil. Gerade für den Anfänger ist es am besten, wenn die eigentliche Funktion nicht durch Tonnen von "Design-Code" überdeckt und unleserlich gemacht wird. Hat man erst einmal die Grundlagen erlernt, so ist es nicht schwer auch ansprechende Oberflächen zu erstellen.
Es gibt eine ganze Reihe von Bibliotheken, die eine weitgehend plattformübergreifende Programmierung mit C++ als Sprache ermöglichen. Aber auch die Programmierung über die API (für engl. „application programming interface“, deutsch: „Schnittstelle zur Anwendungsprogrammierung“) des spezifischen Betriebssystems ist natürlich möglich.
Einsatz von GUI
[Bearbeiten]Sobald man mit C++ dann doch ein GUI programmieren will, sollte auf externe Bibliotheken zurückgegriffen werden. Alle GUIs erfordern ein Verständnis der C++ Grundlagen. Nachfolgend sind einige Bibliotheken zur GUI-Programmierung aufgelistet. Viele der Bibliotheken lassen sich auch mit anderen Programmiersprachen einsetzen.
Qt
[Bearbeiten]Qt ist eine leistungsstarke plattformübergreifende Klassenbibliothek mit Meta-Object Compiler (genannt: moc), die von der finnischen Firma Trolltech entwickelt wurde. Trolltech wurde 2008 von Nokia übernommen, welches jedoch wirtschaftliche Schwierigkeiten hatte und in der Folge Qt 2011 an das finnische Softwareunternehmen Digia verkaufte.
Die Entwicklung wird inzwischen vom Qt Project vorangetrieben, so dass jeder sich daran beteiligen kann. Die Klassenbibliothek ist unter der GNU General Public License (GPL) und der GNU Lesser General Public License (LGPL) lizenziert. Es gibt außerdem eine proprietäre Lizenz von Digia, die allerdings lediglich zusätzlichen technischen Support beinhaltet.
Speziell aufgrund der Veränderungen, die mit Qt 5 und C++11 Einzug in das Framework halten, ist Qt eine der mächtigsten Bibliotheken für C++. Ein weiterer Vorteil von Qt ist die vollständig freie Entwicklungsumgebung Qt Creator, die insbesondere Anfängern das Programmieren leichter macht.
Das Buch „Qt für C++-Anfänger“ gibt einen Einstieg in die Programmierung mit Qt. Es behandelt nicht viele Themen und ist somit wirklich nur zum Kennenlernen des Frameworks geeignet. Dafür sind die vorhandenen Seiten aber auch gut gefüllt.
GTK+
[Bearbeiten]Das GIMP-Toolkit (abgekürzt: GTK+) ist eine freie Komponentenbibliothek, die in C geschrieben ist und unter der GNU Lesser General Public License (LGPL) steht. Um dieses Framework in C++ zu nutzen gibt es ein Framework namens Gtkmm. Wie Qt ist es für viele Plattformen verfügbar.
wxWidgets
[Bearbeiten]wxWidgets ist ein auf C++ basierendes Open-Source-Framework zur plattformunabhängigen Entwicklung von Anwendungen mit grafischer Benutzeroberfläche (GUI). Die wxWidgets-Lizenz ist eine leicht modifizierte LGPL und erlaubt daher ebenfalls die freie Verwendung in proprietärer und freier Software und den weiteren Vertrieb unter einer selbst gewählten Lizenz.
MFC
[Bearbeiten]Die Microsoft Foundation Classes (MFC) sind ein C++ Framework, vor allem zur GUI-Programmierung unter Microsoft Windows. Es verwendet Entwurfsmuster, insbesondere Document-View (Model-View-Controller), Singleton, Bridge und Observer, um Funktionalität der Windows-API zu kapseln. Außerdem gibt es eine enorme Anzahl an Hilfsmakros zur Unterstützung bei RTTI („Runtime Type Information“), Nachrichtenbehandlung, dynamischer Instanziierung etc.. Da sie seit 1992 in jeder -nicht Express- Version von Microsoft Visual C++ enthalten sind ist ihre Verbreitung auf der Windows-Plattform sehr hoch. Die Verwendung ist an eine kommerzielle Lizenz gebunden, den Besitz einer kostenpflichtigen Version von Visual C++, die MFC-Laufzeitumgebung ist kostenlos. Für die GUI-Entwicklung mit C++ -nicht C++/CLI- auf Windows sind die MFC das am weitesten verbreitete Framework das direkt auf der Windows-API aufsetzt.
Konventionen [Bearbeiten]
Konventionen sind Regeln, die in der Programmierung verwendet werden, um die Übersichtlichkeit des Quellcodes zu erhöhen. Diese Regeln werden vom Programmierer festgelegt und entsprechend werden Sie, wenn Sie sich verschiedene Projekte ansehen, sehr wahrscheinlich auch unterschiedliche Konventionen erkennen. Im Laufe des Lebens entwickelt eigentlich jeder Programmierer seine eigenen Konventionen. Daher ist es wichtig, sich auf eine Liste von Konventionen zu einigen, wenn man in einem Projekt mit mehreren Entwicklern zusammenarbeitet. Da dieses Buch nicht ausschließlich von einem Autor geschrieben wird, handelt es sich auch hierbei um ein solches Projekt.
Innerhalb des Buches wird an verschiedenen Stellen auf einzelne Konventionen eingegangen. Dabei werden auch alternative Möglichkeiten zu den hier verwendeten aufgeführt. Wir werden in diesem Kapitel versuchen, eine vollständige Liste der in diesem Buch verwendeten Konventionen aufzustellen. Der unerfahrene Leser wird damit nicht viel anfangen können und kann den Rest des Kapitels daher überspringen, um später darauf zurück zu kommen. Für Co-Autoren sollte die Liste jedoch von großem Wert sein.
- Bezeichner (selbstvergebene Namen, engl. identifier) werden vollständig klein geschrieben. Besteht ein Bezeichner aus mehreren Wörtern, so werden diese mit Unterstrich getrennt.
- Ausnahme: Templateparameter (Vorlagenparameter). Diese beginnen mit einem Großbuchstaben und die Trennung erfolgt wiederum mit einem Großbuchstaben und ohne Unterstrich.
- Die Namen für Quell- (
.cpp
) und Headerdateien (.hpp
) sowie für Unterverzeichnisse folgen den Konventionen für Bezeichner. - Eine Einrückung besteht aus vier Leerzeichen.
- Eingebaute Datentypen werden abgekürzt (
long
stattsigned long int
), Ausnahme:unsigned int
undsigned int
, Achtung:char
ist nicht zwingend gleichsigned char
! - Im globalen Namensraum werden keine
using
-Direktiven verwendet. Eigentlich sollten im globalen Namensraum ausschließlich benannte Namensräume stehen. Das würde im Buch jedoch den Beispielcode aufblähen. Daher verzichten wir ausnahmsweise darauf. - Bezeichner aus anderen Namensräumen sollten per
using
-Deklaration eingefügt werden. Das spart Tipparbeit und hat in Zusammenhang mit Templates (Vorlagen) auch einen technisch sinnvollen Effekt (argumentabhängige Namenssuche). Da dies zwar in der Breite Platz spart, jedoch zusätzliche Zeilen benötigt, wird in unbedenklichen Fällen innerhalb des Buches direkt zugegriffen. Dies gilt insbesondere fürstd::cout
,std::cin
undstd::endl
. - Es werden nur Zeilenkommentare (
// Kommentar
) verwendet. C-Kommentare (/* Kommentar */
) sind tabu. const
steht immer rechts von dem, zu dem es gehört. (int const
stattconst int
)- Templatetypparameter werden durch
typename
gekennzeichnet, nicht durchclass
. - Öffnende geschweifte Klammern stehen nicht auf einer eigenen Zeile, sondern am Zeilenende. Sie werden nicht durch Leerzeichen getrennt.
- Schließende Klammern stehen auf der gleichen Einrückungsebene, wie der Ausdruck der korrespondierenden öffnenden Klammer.
- Es wird maximal eine Anweisung (engl. statement) pro Zeile geschrieben.
- Wird eine einzelne Anweisung nach einem
if
usw. auf eine neue Zeile geschrieben, wird sie in einen Block gepackt, sonst nicht. - Lange Zeilen werden umgebrochen, wobei die umgebrochenen Zeilen um eine Ebene eingerückt sind.
- Funktionsaufrufe werden so umgebrochen, dass die runden Klammern wie Geschweifte und die einzelnen Parameter wie Anweisungen (jeder auf eine eigene Zeile) behandelt werden. Dies bedeutet insbesondere, dass die schließende Klammer auf der gleichen Einrückungsebene steht, wie der Funktionsaufruf.
- Mit Templateinstantiierungen wird analog zu Funktionsaufrufen verfahren.
- Mehrere Variablen werden nur dann gemeinsam deklariert, wenn sie einen identischen Typ haben. (
int a, *b;
ist verboten.) - Die Variablendefinition erfolgt so spät wie möglich.
- Klassen:
private
- undprotected
-Variablen enden auf einen Unterstrich.private
-Funktionen enden auf einen Unterstrich.private
,protected
,public
undfriend
stehen auf der gleichen Einrückungsebene wieclass
.
- Wenn im Fließtext über Funktionen (Methode, Operatoren in Funktionsschreibweise) gesprochen wird, wird dies mit leeren Klammern hinter dem Funktionsnamen angedeutet.
Speziell für Autoren
[Bearbeiten]- Die Vorlagen von C++-Programmierung/ Vorlagen sind zu verwenden.
- Die Vorlage „cpp“ für Fließtext-C++ wird mit einem kleinen „c“ am Anfang geschrieben. Dies ist weniger Tipparbeit und neue Autoren verwirrt eine unterschiedliche Verwendung möglicherweise.
#pragma once
wird in diesem Buch nicht verwendet, weil das nicht im Standard steht.- Quelltext-Blöcke sollten pro Zeile maximal 100 Zeichen umfassen.
- Einige Worte werden auf Englisch belassen. Die folgende Liste versucht alle aufzuführen. Bei umfangreichen Änderungen eines nicht-aufgeführten Wortes, ist vorweg beim Hauptautor (Benutzer:Prog) nachzufragen:
- Compiler (compilergeneriert, Compilierfehler, kompilieren oder übersetzen)
- Code (alternativ Quelltext)
- Bei Konsolenprogrammen wird am Ende nicht auf eine beliebige Taste gewartet;
std::cin.get();
oder garsystem("pause");
(Windows-spezifisch) werden nicht verwendet, weil das überflüssig ist:- Nach der Ausführung in der Konsole ist die Konsole sowieso noch offen; also ist das Arbeitsergebnis zu sehen.
- Bei der Ausführung aus der IDE heraus ist das Warten auf eine Taste Teil der Programmentwicklung und des Testens; also gehört diese Maßnahme nicht in das Programm, sondern zu den Einstellungen der IDE.