C++ oder Java

Wer?

Dies ist Xin's persönliche Meinung zur Frage, ob man mit Java oder C++ als erste Sprache programmieren lernen möchte. Die Argumente gelten soweit nicht anders beschrieben auch für C#.

Background

Ich programmiere seit 1986 und bin Diplom-Informatiker (FH). Ich habe als Java Entwickler gearbeitet und bin derzeit für u.a. 3D Visualisierung in einem CAD Programm mitverantwortlich. Weiterhin entwickle ich die Programmiersprache Genesys, sowie den dazugehörigen Compiler. Meine Diplomarbeit habe ich zum Thema Programmiersprachen und deren Design geschrieben. Ich habe mehreren Dutzend Leuten C und C++ beigebracht, teilweise als Tutor meiner Hochschule, wo ich Studenten mit Java-Erfahrung unterrichtete, teilweise ohne jegliche Programmiererfahrung.

Kurzfassung

Ich rate dazu, zunächst C zu lernen und sobald die Grundkonstrukte bekannt sind, auf C++ umzusteigen, wenn die Entscheidung zwischen C++ und Java/C# steht. Wenn noch überhaupt keine Präferenz zur Programmiersprache existiert, so kann vielleicht der Artikel “Wahl der Programmiersprache“ weiterhelfen.

Als Programmiereinsteiger wird man vieles, das hier besprochen wird, vermutlich noch nicht verstehen. Das sorgt dafür, dass ein Einsteiger anschliessend doch nicht in der Lage ist, die richtige Entscheidung bewusst zu treffen. Wer daher Fragen zu diesem Thema stellen möchte, ist eingeladen, dies im Forum zu tun.

Meine Gründe

Die Argumentation, die ich hier anbringe, mag für manche etwas an den Haaren herbeigezogen wirken, dessen bin ich mir bewusst, doch ich möchte betonen, dass ich diese Meinung nicht auf einen kurzfristigen Lernerfolg ausrichte, sondern auf eine langfristige Qualifikation.

Der Lernprozess

Es gilt die Mär, dass C++ fehleranfälliger wäre als Java. Diese Aussage möchte ich klar verneinen und erklären, wie es zu dieser Aussage kommt.

Beginnt man C++ zu lernen, so wird man bereits bei sehr kleinen Programmen in Fehler hineinlaufen. Auch bei Java wird nicht sofort alles perfekt laufen. Bei Java wird man Exceptions erhalten, die einem einen Hinweis darauf geben, was eigentlich passiert ist und der C++-Programmierer muss sich damit anfreunden, dass sein Programm einfach nur mit „Segmentation fault“ von Linux/MacOS beendet wird, bzw. Windows eine MessageBox öffnet „Das Programm musste beendet werden“.

Hier bekommt der Java-Programmierer direkt einen Hinweis mitgeliefert, somit ist er zunächst im Vorteil, während der C++-Schüler vielleicht frustriert ist, weil er sich die Reaktion nicht erklären kann. Während der Java-Programmierer nun den Fehler beseitigt, muss der C++-Entwickler in sich gehen und ausprobieren, bis er den Fehler findet. In dem Moment lernt er nicht C++, in dem Moment lernt er Programmieren. Während der Java-Entwickler sich also um das Symptom kümmert, muss der C++-Entwickler zwei Qualitäten ausbilden: Er muss zunächst eine gewisse Frustrationstoleranz entwickeln, also den Wunsch das Problem zu lösen statt einfach aufzugeben und er muss versuchen das Problem zu begreifen, zu verstehen.

C++ verzeiht weniger Fehler und bietet dem Anfänger viel mehr Möglichkeiten, in eine Falle zu tappen. Meiner Erfahrung nach bedeutet Programmieren zu lernen nicht, zu lernen, wie man programmiert, sondern zu lernen, wie man nicht programmiert. Ich unterrichte nicht den einen Weg, wie man es richtig macht, sondern die Wege, wie man es nicht macht und warum man diese Wege nicht geht.

Dieser Prozess, die falschen Wege zu verstehen, kann mit einem fähigen Lehrer deutlich weniger frustrierend ausfallen. Die Dinge wirklich kleinlich genau zu behandeln führte in den Tutorien dazu, dass die Studenten mit Java-Vorkenntnissen teilweise Schwierigkeiten hatten die Problemlösungen zu begreifen, weil sie zu einfach war. Es wurde versucht ein kompliziertes Gebilde um die Lösung zu bauen, die das Verständnis erleichtern sollte, statt einfach die Einfachheit der Lösung zu akzeptieren.

C ist wie Mathematik - es wird erst schwer, wenn man gelernt hat, dass es schwer ist - und es bleibt nur solange schwer, wie man glaubt, dass es schwer wäre.

Egal welche Sprache man bevorzugt, um Programmieren zu lernen, muss man die Wege kennen, die ins Verderben führen, in C++, wie auch in Java. In Java kann man sich viele Wege allerdings etwas aufschieben. Der Vorteil ist, dass man zu Beginn nicht gleich derart frustriert wird, sondern schneller zu Erfolgserlebnissen gelangt. Der Frust kommt dann später, wenn man bereits den Glauben entwickelt hat, sich als Fortgeschritten zu bezeichnen. Die Fallgruben, die man als Einsteiger ausgelassen hat, müssen nun in größeren Programmen gemeistert werden. Das macht die Suche aufwändiger und schwieriger.

An dieser Stelle überholen meiner Einschätzung nach die C/C++-Schüler die Java-Schüler wieder. Gewappnet mit einem Erfahrungsschatz, welche Fehler man vermeiden sollte und vor allem der Erkenntnis der eigenen Programmierung kritischer gegenüber zu stehen, programmieren sie effektiver, überlegter und machen weniger Fehler.

Mit den Wegen, die übrig bleiben, ist man frei zu tun, was man will: Ob man sich damit auf die rhetorische Qualität eines Prosa-Romans beschränkt, man die Kunst der Programmierung gut erlernt (z.B. Design Patterns) oder man sich zu einem Dichter für Quelltexte entwickelt, muss jeder für sich entscheiden.

Java zwingt den Entwickler zu einer aufwändigen Fehlerbehandlung mit Exceptions. Die Zwangsfehlerbehandlung - selbst dann, wenn ein Fehler ausgeschlossen ist - behindert die Entwicklung von fehlertoleranten Algorithmen. Anfänger schreiben häufig aufwändige Fehlerbetrachtungen, statt zu erkennen, dass sie einen kurzen Algorithmus schreiben könnten, der nicht fehlschlagen kann. Da Exceptions (die oftmals nicht auftreten können) behandelt werden müssen, kommen viele Anfänger gar nicht mehr auf die Idee, einen Algorithmus zu vereinfachen und damit auch zu beschleunigen. Die Sprache behindert damit die Entwicklung des Schülers.

Der Java Hype und die Konsequenzen

Als ich Java lernte, hieß es, dass Java Fehler vermeidet, weil es keine Pointer enthält. Ich glaube, es gibt heute niemanden mehr, der das behauptet, aber damals wurde das an Hochschulen unterrichtet. Ohne Pointer lassen sich keine nennenswerten sinnvollen Programme schreiben.

Java hat neben den wenigen Primitiven (grundlegende Datentypen wie int, char, die man als guter Entwickler eigentlich auch möglichst nicht nutzen sollte, sondern speziell typisiert, z.B. class Id, class Size…), nur Referenztypen (also Klassen), was in Java Pointer bedeutet, die Java „Referenz“ nennt und mit dem '.'-Operator anspricht, der einem C++-Entwickler eine falsche Sicherheit suggeriert. Fakt ist: C++ kann Klasseninstanzen in Klassen einbetten, bzw. sie lokal auf dem Stack verwalten (RAII). In C++ gibt es Referenzen, die korrekt verwendet, einen Vertrag mitliefern, dass sie auf ein existierendes Objekt zeigen. Man kann sie nicht auf Null prüfen, weil sie nicht Null sein dürfen. Diese Referenzen gibt es in Java nicht. Eingebettete Klasseninstanzen gibt es nicht. Es gibt nur Pointer und Null-Pointer-Exceptions.

Die Tatsache, dass es nur Pointer gibt, führt dazu, dass ein Konstruktor in einer Klasse erstmals sämtliche Referenztypen mit new initialisieren muss. Vergisst man hier etwas - z.B. innerhalb einer Bedingung - treten gelegentlich Fehler auf.

Eingebettete Klassen benötigen unter C++ weder zusätzliche Befehle zur Speicheranforderung, noch können sie uninitialisiert vorliegen, da mindestens der Standard-Konstruktor ausgeführt wird.

Ein weiteres, seinerzeit gehyptes Feature sind die Exceptions. Exceptions sind kein neues Feature von Java, auch C++ unterstützt Exceptions, allerdings werden sie in C++ nur sehr sparsam verwendet.

Exceptions (zu Deutsch „Ausnahmen“) werden in Java aber im Regelfall vergewaltigt. Neben der Fehlerbehandlung wird der normale Ablauf der Algorithmen zur Ausnahme erklärt, falls etwas nicht genauso verläuft, wie sich der Entwickler das gewünscht hat. Möchte man eine Datei öffnen und die Datei ist nicht vorhanden, hat man schon eine Ausnahme. Aber ist das wirklich so ungewöhnlich, dass eine Datei mal nicht verfügbar ist, weil sie z.B. noch nicht angelegt wurde?

Um einen Algorithmus in Java korrekt abzubilden, explodiert ein Programm in Exception-Behandlungen. Auch wenn Exceptions die Stellen verringern, an denen Fehler behandelt werden, so geht mit einer Exception die wichtigste Information eines Fehlers verloren: Wo ist der Fehler passiert? Damit fehlt die Information, was ich bereits erledigt habe, welche Ressourcen habe ich belegt? Bezogen auf die fehlende Datei: Wenn ich 10 Dateien öffnen möchte, bekomme ich auf einmal die Information, dass die Datei nicht geöffnet werden konnte. Welche Datei? Und ist das eine Datei, die mich interessiert, vielleicht darf diese Datei ja fehlen. Das alles muss erneut getestet werden. Dem wird häufig entgegnet, dass man diese Informationen ja der Exception mitgeben kann. Damit wird es aber noch aufwändiger, den Fehlerfall erst zu beschreiben, um dann zu testen, was passiert ist, um dann entsprechend zu reagieren. Betrachte ich den Fehler an der Stelle, wo er passiert, weiß ich ganz genau, wie viele Dateien ich schon gelesen habe und ob die jetzt zu lesende Datei für mich wichtig genug ist, um das als Fehler anzusehen.

In meiner Zeit als Java-Entwickler zeigte sich, dass im Alltag für die korrekte Ausnahmebehandlung keine Zeit bleibt, weil sie extrem aufwändig wird. Dabei möchte ich betonen, dass ich die Fehlerbehandlung anderer Entwickler teilweise nachgeholt habe, weil ich sie für mich brauchte. Sobald der erste jedoch nur noch die Basisklasse weiterreicht, ist jegliche sinnvolle Fehlerbehandlung verloren. Die Java-Entwicklungsumgebung 'Eclipse' schlägt dieses Vorgehen jedoch vor, also wird es gemacht.

Entsprechend wird die Fehlerbehandlung vernachlässigt. Vor allem ist sie nicht abschließend, selbst wenn man sich damit Mühe gibt. So werden Exceptions von Exception-Basisklassen gefangen, die für diese später hinzugekommene Exception nicht erweitert wurden. Das ist ein Bug, aber dieses Problem tritt nun mal auf, wenn man Exceptions nutzt. Der Fehler wird für behandelt erklärt, so dass invalide Objekte weiterleben dürfen und später in eigentlich korrekten Abschnitten Fehler hervorrufen. Diese Fehler verlaufen sich in der Willkürlichkeit und sind extrem schlecht test- oder reproduzierbar, zumal das Programm an einer beliebigen Stelle explodiert, die mit dem Fehler gar nichts zu tun haben muss. Das Verständnis über das große Ganze des Programms geht verloren, wie auch durch Exceptions quer durch das Programm gesprungen wird. Es fehlt ein eindeutiger Ablauf.

Ich habe Java-Entwickler in C/C++ unterrichtet. Das sah mehr aus wie eine Gruppentherapie, weil ich diese Entwickler ausfragen musste über ihr Verständnis von Programmierung, um so herauszufinden, wieso sie Probleme beim Programmieren in C++ haben. Die Vorstellung, die Java-Einsteiger von ihrer abstrakten Maschine entwickeln, ist häufig sehr verschwommen. C/C++ erlaubt diese verschwommene Sicht nicht, das sind die Anfängerfehler, die ein C++-Schüler zuerst verstehen muss. Java erlaubt diese Fehler auch nicht, aber das fällt häufig erst bei größeren Programmen auf, wenn man sich darin verlaufen hat.

bedeutende sprachliche Unterschiede

Const-Correctness

C++ ist semantisch stärker. Dazu gehören Spezialisierungen bei Templates, wie - noch viel wichtiger - Const-Correctness. Ein Feature, das Java vollkommen fehlt und mir in C++ hilft, nicht versehentlich Daten zu modifizieren, an die ich derzeit besser nicht rangehe, z.B. weil ich sie in unveränderter Form noch an einer anderen Stelle benötige. Diese Restriktion ist in Java nicht formulierbar.

Mehrfachvererbung

C++ unterstützt Mehrfachvererbungen. In Java wurden sie entfernt, um das Diamant-Problem zu verhindern. Mehrfachvererbungen oder das Diamant-Problem sind aber kein böser Zauber, sondern ein sehr nützliches Feature. Richtig angewandt, helfen Mehrfachvererbungen Redundanzen (Wiederholungen gleicher Textstellen) im Code zu vermeiden. Weniger Code bedeutet, weniger Fehlermöglichkeiten bei gleichzeitig höherer Testabdeckung.

In Java gibt es stattdessen Interfaces, um das Diamant-Problem zu verhindern. Das Implementieren von Interfaces in Java bedeutet jedoch häufig redundante Code-Abschnitte. Redundanz bedeutet bei Änderungen eine extrem hohe Wahrscheinlichkeit eine redundante Stelle zu übersehen, so dass man schnell einen Bug eingebaut hat. Es gibt nun, sagen wir, 10 richtige Interface-Implementationen und eine Implementation, die übersehen wurde. Dieser Bug tritt scheinbar willkürlich auf, da (fast) alle Implementationen korrekt sind. Nur wenn zufälligerweise die eine fehlerhafte Implementation aufgerufen wird, gibt es plötzlich Probleme.

Bei Mehrfachvererbung gibt es nur eine Stelle. Die ist entweder immer korrekt oder immer falsch und damit leicht zu finden. Ist sie immer falsch, so wird der Fehler auch häufiger auftreten: Man hat also auch eine höhere Testabdeckung des vorhandenen Codes.

Das Diamant-Problem ist bekannt und muss vom C++-Schüler begriffen werden, so dass der Entwickler entscheiden kann, ob er dies als Feature benötigt oder ob er es vermeiden will. Die Situation ist in in C++ über virtuelle Ableitungen exakt kontrollierbar. Dafür muss man natürlich wissen, was man tut. Ansonsten ist das Diamant-Problem in Java/C# rekonstruierbar, anders ausgedrückt: Es ist durch das Verbieten von Mehrfachvererbung nicht gelöst.

Garbage Collection

Ein ebenfalls wichtiger Unterschied ist die Garbage Collection. Der Entwickler muss sich keine Gedanken darüber machen, dass Speicher wieder freigegeben wird. Die Garbage Collection übernimmt das für einen. Das klingt zunächst gut und funktioniert auch für kleine Programme wunderbar, aber so einfach kommt man aus der Geschichte leider dann doch nicht raus sobald man etwas schreibt, was ernstzunehmend ist. Der Garbage Collector läuft irgendwann an und versucht Speicher freizugeben. In dem Moment stockt das Java-Programm und das kann durchaus merklich stocken.

Man stelle sich das bei einem Airbag-Steuerprogramm vor in dem Moment, in dem ein Unfall passiert. Der Garbage Collector springt an, das Auto schlägt in ein Hindernis ein. Bis der Garbage Collector fertig ist, schlägt der Kopf auf das Lenkrad ein. Nun ist der Garbage Collector fertig, das Programm läuft weiter, stellt den Unfall fest und zündet den Airbag. Die Steuerung eines Airbags ist zeitkritisch und damit ernstzunehmen.

Jede Ressource, die zeitkritisch ist (z.B. der Airbag) oder nicht beliebig häufig verfügbar(z.B. ein Drucker, eine Datei), muss sofort wieder freigegeben werden, damit andere Programme sie auch verwenden können. Unterm Strich hilft der Garbage Collector damit leider nur bei kleinen Programmen oder Anwendungen, bei denen es egal ist, ob die Software mal eine Sekunde keine Reaktion zeigt.

Ein Garbage Collector beschränkt sich auf eine gewisse Speichergröße, die beim Programmstart festgelegt wird. Wenn die Speichergröße zu groß wird, wird solange Speicher verbraten, bis dieser Speicher belegt ist und damit hat der Rest des Computers weniger Speicher zur Verfügung und muss auf die viel langsamere Festplatte auslagern. Ist der Puffer für den Garbage Collector zu klein gewählt, so kann das Programm eventuell wegen Speichermangel nicht ausgeführt werden oder stürzt ab, obwohl noch Gigabytes Speicher frei sind. Der Garbage Collector löst ein Anfängerproblem, schafft damit aber neue Probleme: Der Frust kommt später.

Durch die eigene Speicherverwaltung ist das Anfordern von Speicher jedoch schneller und - solange der Garbage Collector nicht anläuft - muss ja auch kein Speicher freigegeben werden. Hier ist Java also schneller als C++ (eben solange der Garbage Collector nicht anläuft). C++ fordert neuen Speicher beim Betriebssystem an, was durch den Kontextwechsel länger dauert (Das Programm fragt das Betriebssystem und muss dann erst einmal warten). Variablen, die auf dem Stack liegen, müssen jedoch nicht vom Betriebssystem angefordert werden und bei eingebettete Klassen wird der Speicher in einem Rutsch mit der umschließenden Klasse angefordert und muss nicht wie in Java pro Variable zusätzlich angefordert werden. Der Vorteil, dass Java schneller Speicher anfordert, ist hier also eher theoretischer Natur. Wird er praktisch relevant, so lässt sich das Speichermanagement in C++ auch durch optimierte, eigene Implementationen übernehmen - für alle Klassen oder auch gezielt für jede einzelne Klasse optimiert. (siehe Real World Comparison, GC vs. Manual Memory Management)

Objektorientierte Programmierung

Objektorientierte Programmierung wird teilweise als Paradigma (grundlegende Programmierweise) verkauft, dann wieder als das Non-Plus-Ultra der Programmierung. Wer immer das behauptet, den muss ich enttäuschen. Objektorientierte Programmierung ist eine Programmiertechnik, sie löst ein bestimmtes Problem, vergleichbar mit Listen, die das Problem lösen, dass man in Arrays nicht ohne Probleme einfügen kann. Man erkauft sich die einfachere Möglichkeit Daten in Listen einzufügen und zu löschen jedoch mit größerem Aufwand eine Liste zu durchsuchen. Genauso zahlt man für objektorientierte Programmierung einen Preis in Form von Rechenleistung: Das Programm wird langsamer.

Die Tatsache, dass man in der Theorie der objektorientierten Programmierung virtuelle Funktionsaufrufe „Messages“ nennt, macht daraus noch kein Weltwunder. Eine virtuelle Funktion, die eine Verbindung zu einem Objekt auf einem anderen Rechner herstellt ändert am Konzept nichts mehr, auf der realen Maschine bleiben es virtuelle Funktionen. In Java ist alles virtuell, bis es mit „final“ zu statischen Aufrufen deklariert wird. In C++ ist alles statisch (das bedeutet, die Methode ist statisch an einem Ort im Speicher und muss nicht erst gesucht werden), bis es zu einer virtuellen Methode erklärt wird. Das Auswerten, welche Methode beim virtuellen Methodenaufruf gewählt wird, ist nicht teuer - aber eben auch nicht umsonst. Wenn man also diese zusätzliche Funktion benötigt, so muss sie in C++ über das Schlüsselwort „virtual“ gekauft werden, in Java muss man mit dem Schlüsselwort „final“ klarstellen, dass man ein unnötiges Extra nicht bezahlen möchte. Das Schlüsselwort „final“ habe ich nur sehr selten in Java-Quellcodes gesehen. Man zahlt also fast immer.

Der virtuelle Zugriff auf eine Methode - also der interessante Teil der objektorientierten Programmierung - ist bei den meisten Methodenzugriffen überflüssig und sollte überlegt gewählt werden. C++ wählt hier den schnellen Weg und erlaubt dem Entwickler, der das Konzept versteht, virtuelle Methoden zu nutzen - Java nimmt grundsätzlich virtuelle Methoden, der Entwickler muss den interessanten Teil der objektorientierten Programmierung nicht verstehen - es funktioniert ja. Es kostet halt nur Rechenzeit, aber wenn man es nicht weiß, kümmert es einen auch nicht.

Der JIT-Compiler

Der Computer kann nur kompilierte Programme ausführen. Java Programme werden in Bytecode ausgeliefert. Der Just-In-Time-Compiler kompiliert ein Java-Programm beim Start des Programms, während ein C++ Programm sofort kompiliert vorliegt. Der Start eines Java-Programms bedeutet also nicht nur das Programm zu laden, sondern zusätzlich auch den JIT-Compiler und schließlich muss das Programm erst kompiliert werden.

Gelegentlich wird gesagt, dass der JIT-Compiler den Programmablauf zur Laufzeit optimieren kann. Das ist richtig. Dafür muss er erkennen, wann es sich lohnt, ein Programm in veränderter Form neu zu kompilieren, damit es schneller ablaufen kann. Die Überwachung, Verwaltung, das Kompilieren und alleine das Laden des JIT-Compilers benötigt dabei bereits mehr Zeit als die meisten Programme zur eigentlichen Berechnung benötigen. Die Chance durch das (aufwändige) Neukompilieren von Teilen des Quellcodes soviel Zeit zu sparen, dass man ein statisch kompiliertes C++-Programm einholt, würde ich mehr als nur gering bezeichnen. Hierfür muss ein Beispiel vermutlich konstruiert werden, im realen Alltag ist das nicht zu erwarten.

Frameworks

Das Java-Framework und .NET bei C# liefern eine große Menge an Funktionalität mit, auf die sofort zugegriffen werden kann. Leider enthalten beide noch viele Sünden aus ihren Anfangstagen - Sünden, die man nicht hätte begehen müssen, da man die Antworten im C++-Umfeld hätte kopieren können, was man dann auch in späteren Versionen teilweise getan hat (Generics decken einen Teil der Template-Funktionalität ab).

Auch C++ kommt mit Sünden aus der Vergangenheit, allerdings waren diese Sünden in der Entstehungszeit von C++ noch wichtig und sind bewusst in Kauf genommen worden. C++ kommt vergleichsweise mit nahezu keiner nennenswerten Funktionalität auf die Platte. Allerdings finden sich in absolut allen Bereichen Erweiterungen, die von C++ aus verwendet werden können. Hier setzt C/C++ gewissermaßen die Messlatte an. Es gibt soviel, dass gar nicht alles mitgeliefert werden kann. Eine Vielzahl von Bibliotheken für graphische Oberflächen, Spieleprogrammierung, 3D-Visualisierung, Mathematik, Datenbanken und vieles mehr sind für C++ verfügbar. Java und C# bieten eine mehr oder weniger komfortable Anbindung an die Funktionalität, die über C++ erreichbar ist. Auch wenn die Frameworks um die beiden Sprachen groß sind, eine Sprache, die keinen Zugriff auf den gigantischen Fundus von Libraries hat, wie sie für C++ verfügbar sind, wäre auch nicht ernstzunehmen.

Mein Fazit

Java hat einen guten Ansatz geliefert, aber scheitert an der Realität. Man hat über ein wichtiges Problem nachgedacht, nämlich die Programmierung zu vereinfachen. Leider ist man dabei meines Erachtens und meiner Erfahrung nach in die falsche Richtung aufgebrochen.

Java selbst ist hochgradig gehypt worden, um das zu erreichen musste C++ schlecht dargestellt werden. Die Folgeproblematiken kommen in den letzten fünf Jahren auf. Ich weiß von großen Unternehmen, die ihre internen Java-Produkte inzwischen wieder abschaffen und mit anderen Sprachen reimplementieren. Der Java Hype ist vorbei. Die Beliebtheit von Java wird jedoch noch einige Jahre anhalten: Bei Leuten, die es nicht besser wissen, bei Entscheidern, die noch die Marketing-Sprüche des letzten Jahrzehnts im Ohr haben und aufgrund der Tatsache, dass die Software, die im letzten Jahrzehnt geschrieben wurde schließlich nicht einfach weggeworfen werden kann. Java zu lernen lohnt also noch, zumal es grundsätzlich keine schlechte Sprache ist. C++ eignet sich zum lernen nur besser, wenn man Java verstehen möchte: „C++ Entwickler wissen, was sie tun.“, sagte man mir als Begründung, warum man sich für mich als Java-Entwickler entschieden hat, obwohl ich als C++-Entwickler in der Zeit noch über keine nennenswerte Java-Erfahrung verfügte.

C++ ist nicht die Antwort auf die Frage, was die Programmiersprache der Zukunft sein wird. C++ ist alt, hat vieles aus C geerbt, was heute nicht mehr wünschenswert ist und ist auch nicht sonderlich fortschrittlich. Aber es ist Java und C# noch deutlich überlegen. Es ist meiner Überzeugung nach näher an zukünftigen Sprachkonzepten als Java oder C# und es erlaubt dem Lernenden nicht, sich mit Halbwissen durchzumogeln. Der Lernende sollte den Frust am Anfang also nicht zwangsläufig mit eigener Unfähigkeit verwechseln oder mit dem Irrglauben, C/C++ wäre schwer. Es gehört einfach zu den Dingen, die man in jeder Sprache früher oder später lernen muss. In C++ lernt man vieles ganz am Anfang, solange die Programme noch überschaubar sind.