C++

Aus besserwiki.de
C++
ISO C++ Logo.svg
Vom C++-Standardisierungsausschuss gebilligtes Logo
Paradigmen Multi-Paradigma: prozedural, imperativ, funktional, objektorientiert, generisch, modular
Familie C
Entworfen von Bjarne Stroustrup
Entwickler ISO/IEC JTC 1 (Gemeinsames Technisches Komitee 1) / SC 22 (Unterkomitee 22) / WG 21 (Arbeitsgruppe 21)
Erstes Erscheinen 1985; vor 37 Jahren
Stabile Version
C++20 (ISO/IEC 14882:2020) / 15. Dezember 2020; vor 20 Monaten
Vorschau-Release
C++23 / 17. März 2022; vor 5 Monaten
Disziplin der Typisierung Statisch, Nominativ, teilweise abgeleitet
OS Plattformübergreifend
Dateinamen-Erweiterungen .C, .cc, .cpp, .cxx, .c++, .h, .H, .hh, .hpp, .hxx, .h++
Wichtigste Implementierungen
GCC, LLVM Clang, Microsoft Visual C++, Embarcadero C++Builder, Intel C++ Compiler, IBM XL C++, EDG
Beeinflusst von
Ada, ALGOL 68, BCPL, C, CLU, ML, Mesa, Modula-2, Simula, Smalltalk
Beeinflusst
Ada 95, C#, C99, Chapel, Clojure, D, Java, JS++, Lua, Nim, Objective-C++, Perl, PHP, Python, Rust, Seed7
  • C++-Programmierung bei Wikibooks

C++ (/ˌsˌplʌsˈplʌs/) ist eine allgemeine Programmiersprache, die vom dänischen Informatiker Bjarne Stroustrup als Erweiterung der Programmiersprache C oder "C mit Klassen" entwickelt wurde. Die Sprache hat sich im Laufe der Zeit erheblich weiterentwickelt, und das moderne C++ verfügt jetzt neben den Möglichkeiten der Speichermanipulation auf niedriger Ebene auch über objektorientierte, generische und funktionale Funktionen. Sie wird fast immer als kompilierte Sprache implementiert, und viele Anbieter bieten C++-Compiler an, darunter die Free Software Foundation, LLVM, Microsoft, Intel, Oracle und IBM, so dass sie auf vielen Plattformen verfügbar ist.

C++ wurde mit Blick auf die Systemprogrammierung und eingebettete, ressourcenbeschränkte Software sowie große Systeme entwickelt, wobei Leistung, Effizienz und Nutzungsflexibilität im Vordergrund standen. C++ hat sich auch in vielen anderen Kontexten als nützlich erwiesen, wobei die Hauptstärken in der Software-Infrastruktur und in ressourcenbeschränkten Anwendungen liegen, darunter Desktop-Anwendungen, Videospiele, Server (z. B. E-Commerce, Websuche oder Datenbanken) und leistungskritische Anwendungen (z. B. Telefonschalter oder Raumsonden).

C++ wird von der Internationalen Organisation für Normung (ISO) standardisiert, wobei die neueste Standardversion von der ISO im Dezember 2020 als ISO/IEC 14882:2020 (informell als C++20 bekannt) ratifiziert und veröffentlicht wurde. Die Programmiersprache C++ wurde ursprünglich 1998 als ISO/IEC 14882:1998 standardisiert, die dann durch die Standards C++03, C++11, C++14 und C++17 ergänzt wurde. Der aktuelle Standard C++20 ersetzt diese mit neuen Funktionen und einer erweiterten Standardbibliothek. Vor der ersten Standardisierung im Jahr 1998 wurde C++ von Stroustrup in den Bell Labs seit 1979 als Erweiterung der Sprache C entwickelt; er wollte eine effiziente und flexible Sprache ähnlich wie C, die auch High-Level-Funktionen für die Programmorganisation bietet. Seit 2012 hat C++ einen dreijährigen Veröffentlichungsrhythmus mit C++23 als nächstem geplanten Standard.

Geschichte

Bjarne Stroustrup, der Schöpfer von C++, in seinem Büro bei AT&T in New Jersey, ca. 2000

1979 begann Bjarne Stroustrup, ein dänischer Informatiker, mit der Arbeit an "C with Classes", dem Vorläufer von C++. Die Motivation, eine neue Sprache zu entwickeln, entstand aus Stroustrups Erfahrungen bei der Programmierung seiner Doktorarbeit. Stroustrup stellte fest, dass Simula zwar über Funktionen verfügte, die für die Entwicklung umfangreicher Software sehr hilfreich waren, die Sprache aber für den praktischen Einsatz zu langsam war, während BCPL zwar schnell, aber zu niedrigstufig war, um für die Entwicklung umfangreicher Software geeignet zu sein. Als Stroustrup in den AT&T Bell Labs zu arbeiten begann, hatte er das Problem, den UNIX-Kernel im Hinblick auf verteiltes Rechnen zu analysieren. Stroustrup erinnerte sich an seine Erfahrungen als Doktorand und machte sich daran, die Sprache C mit Simula-ähnlichen Funktionen zu erweitern. Die Wahl fiel auf C, weil es universell einsetzbar, schnell, portabel und weit verbreitet ist. Neben C und Simula beeinflussten auch andere Sprachen diese neue Sprache, darunter ALGOL 68, Ada, CLU und ML.

Ursprünglich fügte Stroustrups "C mit Klassen" dem C-Compiler Cpre Funktionen hinzu, darunter Klassen, abgeleitete Klassen, starke Typisierung, Inlining und Standardargumente.

Ein Quiz über C++11-Funktionen, das 2015 in Paris gegeben wurde

1982 begann Stroustrup mit der Entwicklung eines Nachfolgers von C with Classes, den er "C++" nannte (

der Inkrement-Operator in C), nachdem er mehrere andere Namen ausprobiert hatte. Es wurden neue Funktionen hinzugefügt, darunter virtuelle Funktionen, Überladung von Funktionsnamen und Operatoren, Referenzen, Konstanten, typsichere Zuweisung von freiem Speicher (new/delete), verbesserte Typüberprüfung und einzeilige Kommentare im Stil von BCPL mit zwei Schrägstrichen (

). Außerdem entwickelte Stroustrup einen neuen, eigenständigen Compiler für C++, Cfront.

Im Jahr 1984 implementierte Stroustrup die erste Stream-Input/Output-Bibliothek. Die Idee, einen Ausgabeoperator anstelle einer benannten Ausgabefunktion bereitzustellen, stammte von Doug McIlroy (der zuvor Unix-Pipes vorgeschlagen hatte).

Im Jahr 1985 wurde die erste Ausgabe von The C++ Programming Language veröffentlicht, die zur maßgeblichen Referenz für die Sprache wurde, da es noch keinen offiziellen Standard gab. Die erste kommerzielle Implementierung von C++ wurde im Oktober desselben Jahres veröffentlicht.

Im Jahr 1989 wurde C++ 2.0 veröffentlicht, gefolgt von der aktualisierten zweiten Ausgabe von The C++ Programming Language im Jahr 1991. Zu den neuen Funktionen in 2.0 gehörten Mehrfachvererbung, abstrakte Klassen, statische Mitgliedsfunktionen, konstante Mitgliedsfunktionen und geschützte Mitglieder. Im Jahr 1990 wurde The Annotated C++ Reference Manual veröffentlicht. Diese Arbeit wurde zur Grundlage für den zukünftigen Standard. Später kamen Templates, Ausnahmen, Namespaces, neue Casts und ein boolescher Typ hinzu.

Im Jahr 1998 wurde C++98 veröffentlicht, das die Sprache standardisierte, und eine kleinere Aktualisierung (C++03) wurde 2003 veröffentlicht.

Nach C++98 entwickelte sich C++ relativ langsam weiter, bis 2011 der Standard C++11 veröffentlicht wurde, der zahlreiche neue Funktionen hinzufügte, die Standardbibliothek weiter vergrößerte und C++-Programmierern mehr Möglichkeiten bot. Nach einer kleineren Aktualisierung von C++14, die im Dezember 2014 veröffentlicht wurde, wurden in C++17 verschiedene neue Ergänzungen eingeführt. Nach der Finalisierung im Februar 2020 wurde ein Entwurf des C++20-Standards am 4. September 2020 verabschiedet und am 15. Dezember 2020 offiziell veröffentlicht.

Am 3. Januar 2018 wurde Stroustrup als Gewinner des Charles Stark Draper Prize for Engineering 2018 bekannt gegeben, "für die Konzeption und Entwicklung der Programmiersprache C++".

Ab 2022 rangiert C++ auf dem TIOBE-Index, einem Maß für die Popularität von Programmiersprachen, an vierter Stelle nach Python, C und Java.

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.

Relativ spät wurden der Sprache Templates, Ausnahmebehandlung, 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.

Etymologie

Laut Stroustrup steht der Name für den evolutionären Charakter der Änderungen gegenüber C. Dieser Name wird Rick Mascitti (Mitte 1983) zugeschrieben und wurde erstmals im Dezember 1983 verwendet. Als Mascitti 1992 inoffiziell zu der Namensgebung befragt wurde, gab er an, dass diese in einem augenzwinkernden Sinne erfolgte. Der Name stammt von C's

Operator (der den Wert einer Variablen inkrementiert) und der üblichen Namenskonvention, ein erweitertes Computerprogramm mit "+" zu bezeichnen.

Während der Entwicklungszeit von C++ wurde die Sprache als "new C" und "C with Classes" bezeichnet, bevor sie ihren endgültigen Namen erhielt.

Philosophie

Während der gesamten Entwicklungszeit von C++ wurde die Sprache von einer Reihe von Prinzipien geleitet:

  • Sie muss sich an tatsächlichen Problemen orientieren, und ihre Funktionen sollten in realen Programmen unmittelbar nützlich sein.
  • Jede Funktion sollte implementierbar sein (mit einer einigermaßen offensichtlichen Möglichkeit, dies zu tun).
  • Programmierer sollten die Freiheit haben, ihren eigenen Programmierstil zu wählen, und dieser Stil sollte von C++ vollständig unterstützt werden.
  • Das Zulassen einer nützlichen Funktion ist wichtiger als das Verhindern jedes möglichen Missbrauchs von C++.
  • C++ sollte Möglichkeiten bieten, Programme in separate, wohldefinierte Teile zu gliedern, und es sollte Möglichkeiten bieten, separat entwickelte Teile zu kombinieren.
  • Keine impliziten Verstöße gegen das Typsystem (aber explizite Verstöße, d.h. solche, die vom Programmierer ausdrücklich verlangt werden, sind erlaubt).
  • Benutzererstellte Typen müssen die gleiche Unterstützung und Leistung wie eingebaute Typen haben.
  • Ungenutzte Funktionen sollten sich nicht negativ auf die erstellten Programme auswirken (z.B. durch geringere Leistung).
  • Es sollte keine Sprache unterhalb von C++ geben (außer Assembler).
  • C++ sollte mit anderen bestehenden Programmiersprachen zusammenarbeiten, anstatt eine eigene, separate und inkompatible Programmierumgebung zu schaffen.
  • Wenn die Absicht des Programmierers nicht bekannt ist, sollte der Programmierer die Möglichkeit haben, sie durch manuelle Kontrolle zu spezifizieren.

Standardisierung

Szene während der Sitzung des C++-Standardisierungsausschusses in Stockholm im Jahr 1996
C++-Standards
Jahr C++-Standard Informeller Name
1998 ISO/IEC 14882:1998 C++98
2003 ISO/IEC 14882:2003 C++03
2011 ISO/IEC 14882:2011 C++11, C++0x
2014 ISO/IEC 14882:2014 C++14, C++1y
2017 ISO/IEC 14882:2017 C++17, C++1z
2020 ISO/IEC 14882:2020 C++20, C++2a

C++ wird von einer ISO-Arbeitsgruppe mit der Bezeichnung JTC1/SC22/WG21 standardisiert. Sie hat bisher sechs Revisionen des C++-Standards veröffentlicht und arbeitet derzeit an der nächsten Revision, C++23.

Im Jahr 1998 standardisierte die ISO-Arbeitsgruppe C++ zum ersten Mal als ISO/IEC 14882:1998, die informell als C++98 bekannt ist. Im Jahr 2003 veröffentlichte sie eine neue Version des C++-Standards mit der Bezeichnung ISO/IEC 14882:2003, in der die in C++98 festgestellten Probleme behoben wurden.

Die nächste größere Überarbeitung des Standards wurde informell als "C++0x" bezeichnet, aber erst 2011 veröffentlicht. C++11 (14882:2011) enthielt zahlreiche Ergänzungen sowohl der Kernsprache als auch der Standardbibliothek.

Im Jahr 2014 wurde C++14 (auch bekannt als C++1y) als kleine Erweiterung von C++11 veröffentlicht, die hauptsächlich Fehlerkorrekturen und kleine Verbesserungen enthielt. Das Abstimmungsverfahren für den Entwurf der internationalen Norm wurde Mitte August 2014 abgeschlossen.

Nach C++14 wurde Mitte Juli 2017 eine größere Revision C++17, informell als C++1z bekannt, vom ISO C++-Komitee fertiggestellt und im Dezember 2017 genehmigt und veröffentlicht.

Als Teil des Standardisierungsprozesses veröffentlicht die ISO auch technische Berichte und Spezifikationen:

  • ISO/IEC TR 18015:2006 über die Verwendung von C++ in eingebetteten Systemen und über die Auswirkungen von C++-Sprach- und Bibliotheksfunktionen auf die Leistung,
  • ISO/IEC TR 19768:2007 (auch bekannt als C++ Technical Report 1) über Bibliothekserweiterungen, die hauptsächlich in C++11 integriert sind,
  • ISO/IEC TR 29124:2010 über spezielle mathematische Funktionen, integriert in C++17
  • ISO/IEC TR 24733:2011 über die dezimale Gleitkommaarithmetik,
  • ISO/IEC TS 18822:2015 über die Standard-Dateisystembibliothek, integriert in C++17
  • ISO/IEC TS 19570:2015 über parallele Versionen der Algorithmen der Standardbibliothek, integriert in C++17
  • ISO/IEC TS 19841:2015 über den Transaktionsspeicher für Software,
  • ISO/IEC TS 19568:2015 über eine neue Reihe von Bibliothekserweiterungen, von denen einige bereits in C++17 integriert sind,
  • ISO/IEC TS 19217:2015 zu den C++-Konzepten, die in C++20 integriert sind
  • ISO/IEC TS 19571:2016 zu den Bibliothekserweiterungen für Gleichzeitigkeit, von denen einige bereits in C++20 integriert sind
  • ISO/IEC TS 19568:2017 über einen neuen Satz von allgemeinen Bibliothekserweiterungen
  • ISO/IEC TS 21425:2017 zu den Bibliothekserweiterungen für Bereiche, die in C++20 integriert sind
  • ISO/IEC TS 22277:2017 über Koroutinen, integriert in C++20
  • ISO/IEC TS 19216:2018 über die Netzwerkbibliothek
  • ISO/IEC TS 21544:2018 über Module, integriert in C++20
  • ISO/IEC TS 19570:2018 über einen neuen Satz von Bibliothekserweiterungen für Parallelität
  • ISO/IEC TS 23619:2021 über neue Erweiterungen für Reflection

Weitere technische Spezifikationen befinden sich in der Entwicklung und müssen noch genehmigt werden, einschließlich einer neuen Reihe von Erweiterungen für Gleichzeitigkeit.

Sprache

Die C++-Sprache besteht aus zwei Hauptkomponenten: einer direkten Abbildung von Hardware-Funktionen, die in erster Linie von der C-Teilmenge bereitgestellt werden, und Abstraktionen ohne Aufwand, die auf diesen Abbildungsfunktionen basieren. Stroustrup beschreibt C++ als "eine leichtgewichtige Abstraktionsprogrammiersprache, die für den Aufbau und die Verwendung effizienter und eleganter Abstraktionen entwickelt wurde"; und "die Grundlage von C++ ist es, sowohl den Hardware-Zugriff als auch die Abstraktion anzubieten. Dies effizient zu tun, ist das, was sie von anderen Sprachen unterscheidet".

C++ erbt den größten Teil der Syntax von C. Im Folgenden sehen Sie Bjarne Stroustrups Version des Programms Hello world, das die Stream-Funktion der C++-Standardbibliothek verwendet, um eine Nachricht auf die Standardausgabe zu schreiben:

Objektspeicherung

Wie in C unterstützt auch C++ vier Arten der Speicherverwaltung: statische Speicherdauerobjekte, Thread-Speicherdauerobjekte, automatische Speicherdauerobjekte und dynamische Speicherdauerobjekte.

Statische Speicherdauerobjekte

Statische Speicherdauerobjekte werden vor der Eingabe von main() erstellt (siehe Ausnahmen unten) und in umgekehrter Reihenfolge der Erstellung nach Beendigung von main() zerstört. Die genaue Reihenfolge der Erstellung ist im Standard nicht festgelegt (obwohl es einige Regeln gibt, die weiter unten definiert sind), um den Implementierungen eine gewisse Freiheit bei der Organisation ihrer Implementierung zu lassen. Formal haben Objekte dieses Typs eine Lebensdauer, die "für die Dauer des Programms andauern soll".

Objekte mit statischer Speicherdauer werden in zwei Phasen initialisiert. Zunächst wird eine "statische Initialisierung" durchgeführt, und erst nachdem alle statischen Initialisierungen erfolgt sind, wird eine "dynamische Initialisierung" durchgeführt. Bei der statischen Initialisierung werden alle Objekte zunächst mit Nullen initialisiert; danach werden alle Objekte, die eine konstante Initialisierungsphase haben, mit dem konstanten Ausdruck initialisiert (d.h. Variablen, die mit einem Literal oder constexpr initialisiert werden). Die statische Initialisierungsphase kann, obwohl sie in der Norm nicht spezifiziert ist, zur Kompilierzeit abgeschlossen und in der Datenpartition der ausführbaren Datei gespeichert werden. Die dynamische Initialisierung umfasst alle Objektinitialisierungen, die über einen Konstruktor oder einen Funktionsaufruf erfolgen (es sei denn, die Funktion ist mit constexpr gekennzeichnet, in C++11). Die Reihenfolge der dynamischen Initialisierung ist definiert als die Reihenfolge der Deklaration innerhalb der Kompilierungseinheit (d. h. derselben Datei). Für die Reihenfolge der Initialisierung zwischen Kompilierungseinheiten gibt es keine Garantien.

Objekte der Thread-Speicherdauer

Variablen dieses Typs sind den statischen Speicherdauerobjekten sehr ähnlich. Der Hauptunterschied besteht darin, dass die Erzeugungszeit unmittelbar vor der Erstellung des Threads liegt und die Zerstörung nach dem Beitritt des Threads erfolgt.

Automatische Speicherdauerobjekte

Die häufigsten Variablentypen in C++ sind lokale Variablen innerhalb einer Funktion oder eines Blocks und temporäre Variablen. Automatischen Variablen ist gemeinsam, dass sie eine Lebensdauer haben, die auf den Geltungsbereich der Variablen beschränkt ist. Sie werden bei der Deklaration erstellt und möglicherweise initialisiert (Einzelheiten siehe unten) und in der umgekehrten Reihenfolge der Erstellung zerstört, wenn der Bereich verlassen wird. Dies wird durch Zuweisung auf dem Stack realisiert.

Lokale Variablen werden erstellt, wenn der Ausführungszeitpunkt den Deklarationszeitpunkt passiert. Wenn die Variable einen Konstruktor oder Initialisierer hat, wird dieser verwendet, um den Anfangszustand des Objekts zu definieren. Lokale Variablen werden zerstört, wenn der lokale Block oder die Funktion, in der sie deklariert sind, geschlossen wird. C++-Destruktoren für lokale Variablen werden am Ende der Lebensdauer des Objekts aufgerufen, was eine in C++ weit verbreitete Disziplin für die automatische Ressourcenverwaltung, RAII genannt, ermöglicht.

Mitgliedsvariablen werden erstellt, wenn das übergeordnete Objekt erstellt wird. Arraymitglieder werden von 0 bis zum letzten Mitglied des Arrays in der Reihenfolge initialisiert. Mitgliedsvariablen werden zerstört, wenn das übergeordnete Objekt in der umgekehrten Reihenfolge der Erstellung zerstört wird, d.h. wenn das übergeordnete Objekt ein "automatisches Objekt" ist, wird es zerstört, wenn es aus dem Anwendungsbereich herausgeht, was die Zerstörung aller seiner Mitglieder auslöst.

Temporäre Variablen werden als Ergebnis einer Ausdrucksauswertung erstellt und zerstört, wenn die Anweisung, die den Ausdruck enthält, vollständig ausgewertet wurde (normalerweise am ; am Ende einer Anweisung).

Objekte mit dynamischer Speicherdauer

Diese Objekte haben eine dynamische Lebensdauer und können direkt mit einem Aufruf von new erzeugt und explizit mit einem Aufruf von delete zerstört werden. C++ unterstützt auch malloc und free, die aus C stammen, aber nicht mit new und delete kompatibel sind. Die Verwendung von new liefert eine Adresse für den zugewiesenen Speicher. Die C++ Core Guidelines raten davon ab, new direkt für die Erstellung dynamischer Objekte zu verwenden, zugunsten von intelligenten Zeigern durch make_unique<T> für Einfachbesitz und make_shared<T> für referenzgezählten Mehrfachbesitz, die in C++11 eingeführt wurden.

Schablonen

C++-Vorlagen ermöglichen eine generische Programmierung. C++ unterstützt Funktions-, Klassen-, Alias- und Variablenvorlagen. Templates können durch Typen, Kompilierzeitkonstanten und andere Templates parametrisiert werden. Schablonen werden durch Instanziierung zur Kompilierzeit implementiert. Um eine Vorlage zu instanziieren, ersetzen die Compiler die Parameter einer Vorlage durch bestimmte Argumente, um eine konkrete Funktion oder Klasseninstanz zu erzeugen. Einige Substitutionen sind nicht möglich; diese werden durch eine Überladungsauflösungspolitik eliminiert, die durch die Formulierung "Substitutionsfehler ist kein Fehler" (SFINAE) beschrieben wird. Schablonen sind ein leistungsfähiges Werkzeug, das für die generische Programmierung, die Schablonen-Metaprogrammierung und die Code-Optimierung verwendet werden kann, aber diese Leistung hat ihren Preis. Die Verwendung von Schablonen kann den Umfang des Codes erhöhen, da jede Schabloneninstanziierung eine Kopie des Schablonencodes erzeugt: eine für jeden Satz von Schablonenargumenten; dies ist jedoch die gleiche oder eine geringere Menge an Code, die erzeugt werden würde, wenn der Code von Hand geschrieben würde. Dies steht im Gegensatz zu den Laufzeit-Generika anderer Sprachen (z. B. Java), bei denen zur Kompilierzeit der Typ gelöscht und ein einziger Schablonenkörper erhalten wird.

Templates unterscheiden sich von Makros: Während diese beiden Sprachfunktionen zur Kompilierzeit eine bedingte Kompilierung ermöglichen, sind Templates nicht auf lexikalische Substitution beschränkt. Schablonen kennen die Semantik und das Typsystem ihrer Begleitsprache sowie alle Typdefinitionen zur Kompilierzeit und können Operationen auf hoher Ebene durchführen, einschließlich der programmatischen Ablaufsteuerung auf der Grundlage der Auswertung streng typgeprüfter Parameter. Makros sind in der Lage, die Kompilierung auf der Grundlage vorher festgelegter Kriterien bedingt zu steuern, können aber keine neuen Typen instanziieren, rekursieren oder eine Typauswertung durchführen und sind somit auf die Textsubstitution vor der Kompilierung und den Einschluss/Ausschluss von Text beschränkt. Mit anderen Worten: Makros können den Kompilierungsfluss auf der Grundlage vordefinierter Symbole steuern, aber im Gegensatz zu Vorlagen keine neuen Symbole unabhängig instanziieren. Schablonen sind ein Werkzeug für statischen Polymorphismus (siehe unten) und generische Programmierung.

Darüber hinaus sind Schablonen ein Kompilierzeit-Mechanismus in C++, der Turing-komplett ist, was bedeutet, dass jede Berechnung, die durch ein Computerprogramm ausgedrückt werden kann, in irgendeiner Form durch ein Schablonen-Metaprogramm vor der Laufzeit berechnet werden kann.

Zusammenfassend lässt sich sagen, dass eine Schablone eine zur Kompilierzeit parametrisierte Funktion oder Klasse ist, die ohne Kenntnis der spezifischen Argumente geschrieben wird, mit denen sie instanziiert wird. Nach der Instanziierung ist der resultierende Code äquivalent zum Code, der speziell für die übergebenen Argumente geschrieben wurde. Auf diese Weise bieten Schablonen eine Möglichkeit, allgemeine, allgemein anwendbare Aspekte von Funktionen und Klassen (die in Schablonen kodiert sind) von spezifischen Aspekten (die in Schablonenparametern kodiert sind) zu entkoppeln, ohne die Leistung aufgrund der Abstraktion zu beeinträchtigen.

Objekte

C++ führt objektorientierte Programmierungsfunktionen (OOP) in C ein. Es bietet Klassen, die die vier in OOP-Sprachen (und einigen Nicht-OOP-Sprachen) üblichen Funktionen bieten: Abstraktion, Kapselung, Vererbung und Polymorphismus. Ein Unterscheidungsmerkmal von C++-Klassen im Vergleich zu Klassen in anderen Programmiersprachen ist die Unterstützung von deterministischen Destruktoren, die wiederum das Konzept "Resource Acquisition is Initialization" (RAII) unterstützen.

Verkapselung

Unter Kapselung versteht man das Verstecken von Informationen, um sicherzustellen, dass Datenstrukturen und Operatoren wie vorgesehen verwendet werden, und um das Verwendungsmodell für den Entwickler offensichtlicher zu machen. C++ bietet die Möglichkeit, Klassen und Funktionen als primäre Kapselungsmechanismen zu definieren. Innerhalb einer Klasse können die Mitglieder entweder als öffentlich, geschützt oder privat deklariert werden, um die Kapselung explizit zu erzwingen. Ein öffentliches Mitglied der Klasse ist für jede Funktion zugänglich. Ein privates Mitglied ist nur für Funktionen zugänglich, die Mitglieder dieser Klasse sind, sowie für Funktionen und Klassen, denen die Klasse ausdrücklich eine Zugriffserlaubnis erteilt hat ("Freunde"). Auf ein geschütztes Mitglied haben neben der Klasse selbst und ihren Freunden auch die Mitglieder der Klassen, die von der Klasse erben, Zugriff.

Das objektorientierte Prinzip gewährleistet die Kapselung aller und nur derjenigen Funktionen, die auf die interne Repräsentation eines Typs zugreifen. C++ unterstützt dieses Prinzip durch Member-Funktionen und Friend-Funktionen, erzwingt es aber nicht. Programmierer können Teile oder die gesamte Repräsentation eines Typs als öffentlich deklarieren, und es ist ihnen erlaubt, öffentliche Entitäten zu schaffen, die nicht Teil der Repräsentation eines Typs sind. Daher unterstützt C++ nicht nur die objektorientierte Programmierung, sondern auch andere Dekompositionsparadigmen wie die modulare Programmierung.

Es gilt allgemein als gute Praxis, alle Daten privat oder geschützt zu machen und nur die Funktionen öffentlich zu machen, die Teil einer minimalen Schnittstelle für die Benutzer der Klasse sind. Auf diese Weise können die Details der Datenimplementierung verborgen werden, so dass der Entwickler die Implementierung später grundlegend ändern kann, ohne die Schnittstelle in irgendeiner Weise zu verändern.

Vererbung

Durch Vererbung kann ein Datentyp die Eigenschaften anderer Datentypen übernehmen. Die Vererbung von einer Basisklasse kann als öffentlich, geschützt oder privat deklariert werden. Dieser Zugriffsspezifizierer bestimmt, ob nicht verwandte und abgeleitete Klassen auf die geerbten öffentlichen und geschützten Mitglieder der Basisklasse zugreifen können. Nur die öffentliche Vererbung entspricht dem, was normalerweise mit "Vererbung" gemeint ist. Die beiden anderen Formen werden viel seltener verwendet. Wird der Zugriffsspezifizierer weggelassen, erbt eine "Klasse" privat, während eine "Struktur" öffentlich erbt. Basisklassen können als virtuell deklariert werden; dies wird als virtuelle Vererbung bezeichnet. Durch die virtuelle Vererbung wird sichergestellt, dass nur eine Instanz einer Basisklasse im Vererbungsgraphen existiert, wodurch einige der Mehrdeutigkeitsprobleme der Mehrfachvererbung vermieden werden.

Mehrfachvererbung ist eine C++-Funktion, die es ermöglicht, dass eine Klasse von mehr als einer Basisklasse abgeleitet werden kann; dadurch sind komplexere Vererbungsbeziehungen möglich. Zum Beispiel kann eine Klasse "Flying Cat" sowohl von "Cat" als auch von "Flying Mammal" erben. Einige andere Sprachen, wie z. B. C# oder Java, erreichen etwas Ähnliches (wenn auch eingeschränkter), indem sie die Vererbung mehrerer Schnittstellen zulassen und gleichzeitig die Anzahl der Basisklassen auf eine beschränken (Schnittstellen bieten im Gegensatz zu Klassen nur Deklarationen von Mitgliedsfunktionen, keine Implementierung oder Mitgliedsdaten). Eine Schnittstelle wie in C# und Java kann in C++ als Klasse definiert werden, die nur rein virtuelle Funktionen enthält und oft als abstrakte Basisklasse oder "ABC" bezeichnet wird. Die Mitgliedsfunktionen einer solchen abstrakten Basisklasse werden normalerweise explizit in der abgeleiteten Klasse definiert und nicht implizit vererbt. Die virtuelle Vererbung in C++ weist eine Funktion zur Auflösung von Mehrdeutigkeiten auf, die Dominanz genannt wird.

Operatoren und Operatorüberladung

Operatoren, die nicht überladen werden können
Operator Symbol
Operator zur Auflösung des Anwendungsbereichs ::
Bedingter Operator ?:
Punkt-Operator .
Operator für Mitgliederauswahl .*
"sizeof"-Operator sizeof
"typeid"-Operator typeid

C++ bietet mehr als 35 Operatoren, die grundlegende arithmetische Operationen, Bitmanipulation, Indirektion, Vergleiche, logische Operationen und andere umfassen. Fast alle Operatoren können für benutzerdefinierte Typen überladen werden, mit einigen bemerkenswerten Ausnahmen wie z. B. Member Access (. und .*) sowie dem Bedingungsoperator. Die Vielzahl der überladbaren Operatoren trägt wesentlich dazu bei, dass benutzerdefinierte Typen in C++ wie eingebaute Typen wirken.

Überladbare Operatoren sind auch ein wesentlicher Bestandteil vieler fortgeschrittener C++-Programmiertechniken, wie z. B. intelligente Zeiger. Das Überladen eines Operators ändert weder die Rangfolge von Berechnungen, die den Operator einbeziehen, noch die Anzahl der Operanden, die der Operator verwendet (jeder Operand kann jedoch vom Operator ignoriert werden, obwohl er vor der Ausführung ausgewertet wird). Überladene "&&"- und "||"-Operatoren verlieren ihre Eigenschaft der Kurzschlussauswertung.

Polymorphismus

Polymorphismus ermöglicht eine gemeinsame Schnittstelle für viele Implementierungen und ein unterschiedliches Verhalten von Objekten unter verschiedenen Umständen.

C++ unterstützt verschiedene Arten von statischen (zur Kompilierzeit aufgelösten) und dynamischen (zur Laufzeit aufgelösten) Polymorphismen, die durch die oben beschriebenen Sprachfunktionen unterstützt werden. Die Polymorphie zur Kompilierzeit lässt bestimmte Entscheidungen zur Laufzeit nicht zu, während die Polymorphie zur Laufzeit in der Regel mit Leistungseinbußen verbunden ist.

Statische Polymorphie

Die Funktionsüberladung ermöglicht es Programmen, mehrere Funktionen mit demselben Namen, aber mit unterschiedlichen Argumenten zu deklarieren (d. h. Ad-hoc-Polymorphismus). Die Funktionen unterscheiden sich durch die Anzahl oder den Typ ihrer formalen Parameter. So kann sich derselbe Funktionsname je nach Kontext, in dem er verwendet wird, auf unterschiedliche Funktionen beziehen. Der von der Funktion zurückgegebene Typ wird nicht verwendet, um überladene Funktionen zu unterscheiden, und unterschiedliche Rückgabetypen würden zu einer Fehlermeldung bei der Kompilierung führen.

Bei der Deklaration einer Funktion kann ein Programmierer für einen oder mehrere Parameter einen Standardwert angeben. Auf diese Weise können die Parameter mit Standardwerten beim Aufruf der Funktion weggelassen werden; in diesem Fall werden die Standardargumente verwendet. Wenn eine Funktion mit weniger Argumenten als deklarierten Parametern aufgerufen wird, werden die expliziten Argumente den Parametern in der Reihenfolge von links nach rechts zugeordnet, wobei allen nicht zugeordneten Parametern am Ende der Parameterliste ihre Standardargumente zugewiesen werden. In vielen Fällen ist die Angabe von Standardargumenten in einer einzigen Funktionsdeklaration der Bereitstellung überladener Funktionsdefinitionen mit einer unterschiedlichen Anzahl von Parametern vorzuziehen.

Templates in C++ bieten einen ausgeklügelten Mechanismus zum Schreiben von generischem, polymorphem Code (d. h. parametrischer Polymorphie). Insbesondere durch das merkwürdigerweise wiederkehrende Template-Muster ist es möglich, eine Form des statischen Polymorphismus zu implementieren, die der Syntax für das Überschreiben virtueller Funktionen sehr ähnlich ist. Da C++-Schablonen typbewusst und Turing-komplett sind, können sie auch dazu verwendet werden, rekursive Konditionale aufzulösen und durch Schablonen-Metaprogrammierung umfangreiche Programme zu erzeugen. Entgegen mancher Meinung erzeugt Vorlagencode nach der Kompilierung mit den richtigen Compiler-Einstellungen keinen Massencode.

Dynamischer Polymorphismus

Vererbung

Variablenzeiger und Verweise auf einen Basisklassentyp in C++ können sich auch auf Objekte beliebiger abgeleiteter Klassen dieses Typs beziehen. Dadurch können Arrays und andere Arten von Containern Zeiger auf Objekte unterschiedlicher Typen enthalten (Referenzen können nicht direkt in Containern gehalten werden). Dies ermöglicht einen dynamischen Polymorphismus (zur Laufzeit), bei dem sich die referenzierten Objekte je nach ihrem (tatsächlichen, abgeleiteten) Typ unterschiedlich verhalten können.

C++ bietet auch den

Operator, der es dem Code ermöglicht, auf sichere Weise zu versuchen, ein Objekt über einen Basisverweis/Zeiger in einen abgeleiteten Typ umzuwandeln: Downcasting. Dieser Versuch ist notwendig, da man oft nicht weiß, welcher abgeleitete Typ referenziert wird. (Upcasting, die Konvertierung in einen allgemeineren Typ, kann immer zur Kompilierzeit geprüft/ausgeführt werden über

durchgeführt werden, da die Vorgängerklassen in der Schnittstelle der abgeleiteten Klasse angegeben sind, die für alle Aufrufer sichtbar ist.)

stützt sich auf Runtime Type Information (RTTI), Metadaten im Programm, die eine Unterscheidung der Typen und ihrer Beziehungen ermöglichen. Wenn ein

zu einem Zeiger fehlschlägt, ist das Ergebnis die

Konstante, während der Cast eine Ausnahme auslöst, wenn das Ziel eine Referenz ist (die nicht null sein kann). Objekte, von denen bekannt ist, dass sie von einem bestimmten abgeleiteten Typ sind, können in diesen gecastet werden mit

unter Umgehung von RTTI und der sicheren Laufzeit-Typüberprüfung von

Dies sollte nur verwendet werden, wenn der Programmierer sicher ist, dass der Cast gültig ist und immer gültig sein wird.

Virtuelle Mitgliedsfunktionen

Wenn eine Funktion in einer abgeleiteten Klasse eine Funktion in einer Basisklasse überschreibt, wird die aufzurufende Funktion normalerweise durch den Typ des Objekts bestimmt. Eine bestimmte Funktion ist überschrieben, wenn es keinen Unterschied in der Anzahl oder dem Typ der Parameter zwischen zwei oder mehreren Definitionen dieser Funktion gibt. Daher kann es zur Kompilierzeit nicht möglich sein, den Typ des Objekts und damit die richtige aufzurufende Funktion zu bestimmen, wenn nur ein Basisklassenzeiger vorhanden ist; die Entscheidung wird daher auf die Laufzeit verschoben. Dies wird als dynamisches Dispatching bezeichnet. Virtuelle Mitgliedsfunktionen oder Methoden ermöglichen es, die spezifischste Implementierung der Funktion aufzurufen, je nach dem tatsächlichen Laufzeittyp des Objekts. In C++-Implementierungen wird dies üblicherweise über virtuelle Funktionstabellen realisiert. Wenn der Objekttyp bekannt ist, kann dies umgangen werden, indem dem Funktionsaufruf ein voll qualifizierter Klassenname vorangestellt wird, aber im Allgemeinen werden Aufrufe von virtuellen Funktionen zur Laufzeit aufgelöst.

Zusätzlich zu den Standard-Member-Funktionen können auch Operator-Überladungen und Destruktoren virtuell sein. Eine ungenaue Regel, die auf praktischer Erfahrung beruht, besagt, dass, wenn eine Funktion in der Klasse virtuell ist, auch der Destruktor virtuell sein sollte. Da der Typ eines Objekts bei seiner Erzeugung zur Kompilierzeit bekannt ist, können Konstruktoren und damit auch Kopierkonstruktoren nicht virtuell sein. Nichtsdestotrotz kann eine Situation entstehen, in der eine Kopie eines Objekts erstellt werden muss, wenn ein Zeiger auf ein abgeleitetes Objekt als Zeiger auf ein Basisobjekt übergeben wird. In einem solchen Fall besteht eine übliche Lösung darin, eine

(oder eine ähnliche) virtuelle Funktion zu erstellen, die beim Aufruf eine Kopie der abgeleiteten Klasse erstellt und zurückgibt. Eine Mitgliedsfunktion kann auch "rein virtuell" gemacht werden, indem man sie mit

nach der schließenden Klammer und vor dem Semikolon. Eine Klasse, die eine rein virtuelle Funktion enthält, nennt man eine abstrakte Klasse. Aus einer abstrakten Klasse können keine Objekte erstellt werden; sie können nur abgeleitet werden. Jede abgeleitete Klasse erbt die virtuelle Funktion als rein und muss eine nicht-reine Definition dieser Funktion (und aller anderen reinen virtuellen Funktionen) bereitstellen, bevor Objekte der abgeleiteten Klasse erstellt werden können. Ein Programm, das versucht, ein Objekt einer Klasse mit einer reinen virtuellen Mitgliedsfunktion oder einer geerbten reinen virtuellen Mitgliedsfunktion zu erstellen, ist nicht formgerecht.

Lambda-Ausdrücke

C++ bietet Unterstützung für anonyme Funktionen, die auch als Lambda-Ausdrücke bezeichnet werden, in der folgenden Form:

Seit C++20 können Sie Template-Parameter in ein Lambda ohne das Schlüsselwort Template schreiben:

Wenn der Lambda-Ausdruck keine Parameter annimmt und kein Rückgabetyp oder andere Spezifizierer verwendet werden, kann das () weggelassen werden, das heißt,

Der Rückgabetyp eines Lambda-Ausdrucks kann, wenn möglich, automatisch abgeleitet werden, z.B.:

Die

Liste unterstützt die Definition von Closures. Solche Lambda-Ausdrücke sind in der Norm als syntaktischer Zucker für ein unbenanntes Funktionsobjekt definiert.

Behandlung von Ausnahmen

Die Ausnahmebehandlung dient dazu, das Vorhandensein eines Laufzeitproblems oder -fehlers von der Stelle, an der es entdeckt wurde, an die Stelle weiterzuleiten, an der das Problem behandelt werden kann. Sie ermöglicht es, dies auf einheitliche Weise und getrennt vom Hauptcode zu tun und dabei alle Fehler zu erkennen. Tritt ein Fehler auf, wird eine Ausnahme ausgelöst (raise), die dann vom nächstgelegenen geeigneten Exception-Handler abgefangen wird. Die Ausnahme bewirkt, dass der aktuelle Bereich verlassen wird und auch jeder äußere Bereich (Propagation), bis ein geeigneter Handler gefunden wird, der wiederum die Destruktoren aller Objekte in diesen verlassenen Bereichen aufruft. Gleichzeitig wird eine Ausnahme als ein Objekt dargestellt, das die Daten über das erkannte Problem enthält.

Einige C++-Styleguides, wie der von Google, LLVM und Qt, verbieten die Verwendung von Ausnahmen.

Der die Ausnahme verursachende Code befindet sich in einem

Block platziert. Die Ausnahmen werden in separaten

Blöcken (den Handlern) behandelt; jeder

Block kann mehrere Ausnahmebehandler haben, wie im Beispiel unten zu sehen ist.

Es ist auch möglich, Ausnahmen gezielt auszulösen, indem man das

Schlüsselwort; diese Ausnahmen werden auf die übliche Weise behandelt. In einigen Fällen können Ausnahmen aus technischen Gründen nicht verwendet werden. Ein solches Beispiel ist eine kritische Komponente eines eingebetteten Systems, bei dem garantiert werden muss, dass jeder Vorgang innerhalb einer bestimmten Zeitspanne abgeschlossen wird. Dies kann mit Ausnahmen nicht bestimmt werden, da es keine Werkzeuge gibt, um die maximale Zeit zu bestimmen, die für die Behandlung einer Ausnahme erforderlich ist.

Im Gegensatz zur Signalbehandlung, bei der die Behandlungsfunktion an der Fehlerstelle aufgerufen wird, verlässt die Ausnahmebehandlung den aktuellen Bereich, bevor der Catch-Block eingegeben wird, der sich in der aktuellen Funktion oder in einem der vorherigen Funktionsaufrufe befinden kann, die sich derzeit auf dem Stack befinden.

Standardbibliothek

Der Entwurf des "Arbeitspapiers" zum Standard, der als C++98 angenommen wurde; die Hälfte seines Umfangs wurde der C++-Standardbibliothek gewidmet.

Der C++-Standard besteht aus zwei Teilen: der Kernsprache und der Standardbibliothek. C++-Programmierer erwarten letztere bei jeder größeren C++-Implementierung; sie umfasst Aggregattypen (Vektoren, Listen, Maps, Mengen, Warteschlangen, Stapel, Arrays, Tupel), Algorithmen (find, for_each, binary_search, random_shuffle usw. ), Ein-/Ausgabemöglichkeiten (iostream, zum Lesen von und Schreiben auf die Konsole und in Dateien), Dateisystembibliothek, Lokalisierungsunterstützung, intelligente Zeiger für die automatische Speicherverwaltung, Unterstützung für reguläre Ausdrücke, Multithreading-Bibliothek, Atomics-Unterstützung (die es ermöglicht, dass eine Variable von höchstens einem Thread gleichzeitig gelesen oder beschrieben werden kann, ohne dass eine externe Synchronisierung erforderlich ist), Zeitdienstprogramme (Messung, Abfrage der aktuellen Zeit usw.), ein System zur Konvertierung von Fehlern in eine andere Zeit. ), ein System zur Konvertierung von Fehlerberichten, die keine C++-Ausnahmen verwenden, in C++-Ausnahmen, einen Zufallszahlengenerator und eine leicht modifizierte Version der C-Standardbibliothek (um sie mit dem C++-Typensystem kompatibel zu machen).

Ein großer Teil der C++-Bibliothek basiert auf der Standard Template Library (STL). Zu den nützlichen Werkzeugen der STL gehören Container als Sammlungen von Objekten (wie Vektoren und Listen), Iteratoren, die einen Array-ähnlichen Zugriff auf Container ermöglichen, und Algorithmen, die Operationen wie Suchen und Sortieren durchführen.

Darüber hinaus werden (multi)maps (assoziative Arrays) und (multi)sets bereitgestellt, die alle kompatible Schnittstellen exportieren. Mit Hilfe von Templates ist es daher möglich, generische Algorithmen zu schreiben, die mit jedem Container oder jeder durch Iteratoren definierten Sequenz arbeiten. Wie in C erfolgt der Zugriff auf die Funktionen der Bibliothek mit Hilfe der

Direktive zur Einbindung eines Standard-Headers. Die C++-Standardbibliothek bietet 105 Standardheader, von denen 27 veraltet sind.

Der Standard enthält die STL, die ursprünglich von Alexander Stepanov entworfen wurde, der viele Jahre lang mit generischen Algorithmen und Containern experimentierte. Als er mit C++ begann, fand er schließlich eine Sprache, in der es möglich war, generische Algorithmen (z. B. STL-Sort) zu erstellen, die dank C++-Funktionen wie Inlining und Bindung zur Kompilierzeit anstelle von Funktionszeigern sogar besser funktionieren als z. B. die C-Standardbibliothek qsort. In der Norm wird sie nicht als "STL" bezeichnet, da sie lediglich ein Teil der Standardbibliothek ist, aber der Begriff wird immer noch häufig verwendet, um sie vom Rest der Standardbibliothek zu unterscheiden (Eingabe-/Ausgabeströme, Internationalisierung, Diagnose, die Teilmenge der C-Bibliothek usw.).

Die meisten C++-Compiler, und zwar alle wichtigen, bieten eine standardkonforme Implementierung der C++-Standardbibliothek.

C++-Kernrichtlinien

Die C++ Core Guidelines sind eine Initiative unter der Leitung von Bjarne Stroustrup, dem Erfinder von C++, und Herb Sutter, dem Einberufer und Vorsitzenden der C++ ISO-Arbeitsgruppe. Sie sollen Programmierern dabei helfen, "modernes C++" zu schreiben, indem sie bewährte Praktiken für die Sprachstandards C++11 und neuer anwenden, und Entwicklern von Compilern und statischen Prüfwerkzeugen dabei helfen, Regeln zum Aufspüren schlechter Programmierpraktiken zu erstellen.

Das Hauptziel ist es, effizient und konsistent typ- und ressourcensicheres C++ zu schreiben.

Die Core Guidelines wurden in der Eröffnungs-Keynote der CPPCon 2015 angekündigt.

Die Richtlinien werden von der Guideline Support Library (GSL) begleitet, einer reinen Header-Bibliothek mit Typen und Funktionen zur Implementierung der Core Guidelines und statischen Prüfwerkzeugen zur Durchsetzung der Richtlinienregeln.

Kompatibilität

Die Implementierung eines C++-Compilers gilt als aufwendig. Nach der Fertigstellung der Sprachnorm 1998 dauerte es mehrere Jahre, bis die Sprache von C++-Compilern weitestgehend unterstützt wurde.

Zu den verbreitetsten C++-Compilern gehören:

Visual C++
Der in Microsoft Visual C++ enthaltene Compiler ist der am weitesten verbreitete für das Betriebssystem Windows. Die Community-Edition stellt Microsoft kostenlos zur Verfügung.
GCC
Der g++ ist die C++-Ausprägung der GNU Compiler Collection (GCC); g++ ist quelloffen und frei verfügbar. Der g++ unterstützt eine Vielzahl von Betriebssystemen (darunter Unix, Linux, macOS, Windows und AmigaOS) und Prozessorplattformen. GNU C++ existiert seit 1987 und ist somit einer der ältesten C++-Compiler.
Intel C++ Compiler
Der Intel C++ Compiler verwendet ebenfalls das erwähnte C++-Front-End von EDG. Der Intel C++ Compiler erzeugt Maschinencode für die Intel-Prozessoren unter den Betriebssystemen Windows, Linux und macOS. Da die mit dem Intel C++ Compiler erzeugten Programme den Befehlssatz der Intel-Prozessoren besonders gut ausnutzen, erzeugen sie besonders effiziente Programme für diese Plattform. (Kompilate des Intel-Compilers laufen ebenfalls auf AMD-Chips meist schneller als Kompilate der alternativen Compiler, entsprechende Optimierungsflags sperrt Intel jedoch, wobei sich die Sperre aufheben lässt.) Der Intel C++ Compiler nutzt im Unterbau wesentliche Teile des g++ und ersetzt und erweitert Teile der Code-Optimierung und Code-Generierung.
Clang
Clang, ein Frontend für die von Apple geförderte plattformübergreifende Compilerinfrastruktur LLVM, die unter anderem auch in der integrierten Entwicklungsumgebung Xcode verwendet wird.
Oracle Solaris Studio
Oracle Solaris Studio stellt Oracle kostenlos zur Verfügung.
Comeau C++
Der Comeau C++. Das sogenannte „Front-End“ des Compilers, also der Teil, der die Analyse-Phase implementiert, wurde von der Firma Edison Design Group (EDG) entwickelt, die sich auf die Entwicklung von Compiler-Front-Ends spezialisiert hat und deren C++-Front-End auch in vielen anderen kommerziellen C++-Compilern integriert ist. Der Comeau-Compiler kann auch über das Internet ausprobiert werden.
Turbo C++
Mit Turbo C++/C++ Builder steht ein weiterer Compiler zur Verfügung.

Um den Anbietern von Compilern mehr Freiheit zu geben, beschloss das C++-Standardisierungskomitee, die Implementierung von Namensmangling, Ausnahmebehandlung und anderen implementierungsspezifischen Funktionen nicht vorzuschreiben. Der Nachteil dieser Entscheidung ist, dass Objektcode, der von verschiedenen Compilern erzeugt wird, voraussichtlich nicht kompatibel ist. Es gab jedoch Versuche, Compiler für bestimmte Maschinen oder Betriebssysteme zu standardisieren (z. B. C++ ABI), doch scheinen diese inzwischen weitgehend aufgegeben worden zu sein.

Mit C

C++ wird oft als Obermenge von C betrachtet, aber das ist nicht ganz richtig. Der meiste C-Code lässt sich leicht in C++ korrekt kompilieren, aber es gibt einige Unterschiede, die dazu führen, dass gültiger C-Code ungültig ist oder sich in C++ anders verhält. Zum Beispiel erlaubt C die implizite Konvertierung von

in andere Zeigertypen, was in C++ nicht der Fall ist (aus Gründen der Typsicherheit). Außerdem definiert C++ viele neue Schlüsselwörter, wie z.B.

und

, die in einem C-Programm als Bezeichner (z. B. Variablennamen) verwendet werden können. Einige Inkompatibilitäten wurden mit der 1999 erfolgten Überarbeitung des C-Standards (C99) beseitigt, der nun auch C++-Funktionen wie Zeilenkommentare (

) und mit Code vermischte Deklarationen. Andererseits führte C99 eine Reihe neuer Funktionen ein, die C++ nicht unterstützte und die in C++ inkompatibel oder überflüssig waren, wie z. B. Arrays mit variabler Länge, native komplexe Zahlentypen (allerdings bietet die

Klasse in der C++-Standardbibliothek bietet ähnliche Funktionalität, wenn auch nicht codekompatibel), designierte Initialisierer, zusammengesetzte Literale und das

Schlüsselwort. Einige der in C99 eingeführten Funktionen wurden in die nachfolgende Version des C++-Standards, C++11, übernommen (von denen, die nicht redundant waren). Der C++11-Standard führt jedoch neue Inkompatibilitäten ein, wie z. B. das Verbot der Zuweisung eines Zeichenkettenliterales an einen Zeichenzeiger, das in C weiterhin gültig ist. Um C- und C++-Code miteinander zu vermischen, muss jede Funktionsdeklaration oder -definition, die sowohl in C als auch in C++ aufgerufen/verwendet werden soll, mit C-Verknüpfung deklariert werden, indem sie in einen

Block platziert wird. Eine solche Funktion darf sich nicht auf Funktionen stützen, die auf Namensmanipulation beruhen (d. h. Funktionsüberladung).

Kritik

Trotz ihrer weiten Verbreitung haben einige namhafte Programmierer die Sprache C++ kritisiert, darunter Linus Torvalds, Richard Stallman, Joshua Bloch, Ken Thompson und Donald Knuth.

Einer der häufigsten Kritikpunkte an C++ ist die empfundene Komplexität der Sprache, wobei kritisiert wird, dass eine große Anzahl nicht-orthogonaler Funktionen in der Praxis eine Beschränkung des Codes auf eine Teilmenge von C++ erforderlich macht, wodurch die Vorteile der Lesbarkeit eines gemeinsamen Stils und gemeinsamer Idiome nicht genutzt werden können. Wie von Joshua Bloch ausgedrückt:

Ich denke, C++ wurde weit über seine Komplexitätsschwelle hinaus getrieben, und dennoch gibt es eine Menge Leute, die es programmieren. Aber man zwingt die Leute dazu, es zu unterteilen. Fast jeder Laden, den ich kenne, der C++ verwendet, sagt: "Ja, wir verwenden C++, aber wir verwenden keine Mehrfachimplementierungsvererbung und keine Operatorüberladung." Es gibt einfach eine ganze Reihe von Funktionen, die man nicht verwenden wird, weil die Komplexität des resultierenden Codes zu hoch ist. Und ich glaube nicht, dass es gut ist, wenn man das tun muss. Man verliert die Portabilität der Programmierer, wo jeder den Code von jedem lesen kann, was ich für eine gute Sache halte.

Donald Knuth (1993, in einem Kommentar zu vorstandardisiertem C++), der über Edsger Dijkstra sagte, dass "der Gedanke an Programmierung in C++" "ihn körperlich krank machen würde":

Das Problem, das ich heute mit ihnen habe, ist, dass ... C++ zu kompliziert ist. Im Moment ist es für mich unmöglich, portablen Code zu schreiben, von dem ich glaube, dass er auf vielen verschiedenen Systemen funktioniert, es sei denn, ich vermeide alle exotischen Funktionen. Wann immer die C++-Sprachentwickler zwei konkurrierende Ideen hatten, wie sie ein Problem lösen sollten, sagten sie "OK, wir machen beides". Deshalb ist die Sprache für meinen Geschmack zu barock.

Ken Thompson, ein Kollege von Stroustrup bei Bell Labs, gibt seine Einschätzung ab:

Sie hat sicherlich ihre guten Seiten. Aber im Großen und Ganzen halte ich sie für eine schlechte Sprache. Sie macht eine Menge Dinge halbwegs gut und ist einfach ein Müllhaufen von Ideen, die sich gegenseitig ausschließen. Jeder, den ich kenne, ob privat oder im Unternehmen, wählt eine Teilmenge aus, und diese Teilmengen sind unterschiedlich. Es ist also keine gute Sprache, um einen Algorithmus zu transportieren und zu sagen: "Ich habe ihn geschrieben; hier, nimm ihn." Er ist viel zu groß, viel zu komplex. Und sie wurde offensichtlich von einem Komitee entwickelt. Stroustrup hat sich jahrelang dafür eingesetzt, dass die Sprache angenommen und verwendet wird, und zwar weit über seine technischen Beiträge hinaus. Und er leitete alle Normenausschüsse mit einem Einpeitscher und einem Vorsitzenden. Und er sagte zu niemandem "Nein". Er fügte jede Funktion in diese Sprache ein, die es je gab. Sie war nicht sauber entworfen - sie war einfach die Vereinigung von allem, was da war. Und ich glaube, dass sie darunter sehr gelitten hat.

Brian Kernighan, ebenfalls ein Kollege bei Bell Labs, widerspricht jedoch dieser Einschätzung:

C++ hat einen enormen Einfluss gehabt. ... Viele Leute sagen, C++ sei zu groß und zu kompliziert usw. usw., aber in Wirklichkeit ist es eine sehr mächtige Sprache, und so ziemlich alles, was darin enthalten ist, hat einen wirklich guten Grund: Es ist nicht irgendjemand, der willkürliche Erfindungen macht, sondern es sind tatsächlich Leute, die versuchen, Probleme der realen Welt zu lösen. Viele der Programme, die wir heute als selbstverständlich ansehen und einfach benutzen, sind C++-Programme.

Stroustrup selbst sagt, dass die Semantik von C++ viel sauberer ist als seine Syntax: "Innerhalb von C++ gibt es eine viel kleinere und sauberere Sprache, die darum kämpft, herauszukommen".

Andere Beschwerden betreffen das Fehlen von Reflection oder Garbage Collection, die langen Kompilierungszeiten, die schleichende Zunahme von Funktionen und die ausführlichen Fehlermeldungen, insbesondere bei der Template-Metaprogrammierung.

Oft geäußerte Kritik an der Sprache umfasst beispielsweise:

  • C++ sei sehr komplex und fehleranfällig zu programmieren. Man müsse viel lernen und üben, um es gut zu beherrschen, und viele Features gelten als äußerst komplex.
  • C++ sei zu low-level-mäßig aufgebaut; während es viele Features von höher abstrahierenden Sprachen aufweist (Klassen, generische Klassen/Funktionen etc.), seien als wichtig empfundene Dinge, insbesondere Garbage Collection, nicht vorhanden.
  • C++ gilt zwar als schnell, beispielsweise wegen der Möglichkeit, frei mit Pointern zu arbeiten, doch diese Leistung sei auf den heutigen, schnellen Computersystemen nur in Ausnahmefällen nötig: Während es sinnvoll sei, Betriebssysteme o. Ä. in C++ zu schreiben, sei es softwaretechnisch viel günstiger, Anwendungsprogramme in höheren Sprachen zu schreiben, da diese leichter zu warten seien und immer noch eine ausreichende Leistung aufwiesen.
  • Typisch in Verbindung mit C++ ist das Zitat von Bjarne Stroustrup:

“In C++ it’s harder to shoot yourself in the foot, but when you do, you blow off your whole leg.”

„In C++ ist es schwieriger, sich selbst in den Fuß zu schießen, aber wenn man es tut, dann ist gleich das ganze Bein weg.“

Soll heißen: C++ erleichtert zunächst vieles, aber es bringt gleichzeitig viele Mittel mit sich, die mit Bedacht eingesetzt werden müssen. Zum Beispiel können durch die dynamische Speicherallokation ohne automatische Speicherfreigabe Speicherlecks entstehen. Zeiger können auf falsche Speicherbereiche verweisen und verstecktes Fehlverhalten erzeugen (Hängender Zeiger).

Einsatzgebiete

C++ wird sowohl in der Systemprogrammierung als auch in der Anwendungsprogrammierung eingesetzt und gehört in beiden Bereichen zu den verbreitetsten Programmiersprachen.

Systemprogrammierung

Typische Anwendungsfelder in der Systemprogrammierung sind Betriebssysteme, eingebettete Systeme, virtuelle Maschinen, Treiber und Signalprozessoren. C++ nimmt hier oft den Platz ein, der früher ausschließlich Assemblersprachen und der Programmiersprache C vorbehalten war.

Anwendungsprogrammierung

Bei der Anwendungsprogrammierung kommt C++ vor allem dort zum Einsatz, wo hohe Anforderungen an die Effizienz gestellt werden, um durch technische Rahmenbedingungen vorgegebene Leistungsgrenzen möglichst gut auszunutzen. Ab dem Jahr 2000 wurde C++ aus der Domäne der Anwendungsprogrammierung von den Sprachen Java und C# zurückgedrängt.

Eigenschaften

Sprachdesign

Die Sprache C++ verwendet nur etwa 60 Schlüsselwörter („Sprachkern“), manche werden in verschiedenen Kontexten (static, default) mehrfach verwendet. Ihre eigentliche Funktionalität erhält sie, ähnlich wie auch die Sprache C, durch die C++-Standardbibliothek, die der Sprache fehlende wichtige Funktionalitäten beibringt (Arrays, Vektoren, Listen, …) wie auch die Verbindung zum Betriebssystem herstellt (iostream, fopen, exit, …). Je nach Einsatzgebiet kommen weitere Bibliotheken und Frameworks dazu. C++ legt einen Schwerpunkt auf die Sprachmittel zur Entwicklung von Bibliotheken. Dadurch favorisiert es verallgemeinerte Mechanismen für typische Problemstellungen und besitzt kaum in die Sprache integrierte Einzellösungen.

Eine der Stärken von C++ ist die Kombinierbarkeit von effizienter, maschinennaher Programmierung mit mächtigen Sprachmitteln, die einfache bis komplexe Implementierungsdetails zusammenfassen und weitgehend hinter abstrakten Befehlsfolgen verbergen. Dabei kommt vor allem die Template-Metaprogrammierung zum Zuge: Eine Technik, die eine nahezu kompromisslose Verbindung von Effizienz und Abstraktion erlaubt.

Einige Design-Entscheidungen werden allerdings auch häufig kritisiert:

Ressourcenverwaltung

C++ hat keine Garbage Collection, allerdings gibt es Bestrebungen, Garbage-Collection durch Bibliotheken oder durch Aufnahme in den Sprachstandard zu ermöglichen. Siehe auch Boehm-Speicherbereinigung.

Es ist jedoch möglich, Speicher im Programm zu verwalten; zur Implementierung von Low-Level-Bibliotheken wie der C++-Standardbibliothek ist es notwendig. In High-Level-Code wird hiervon jedoch dringend abgeraten.

Stattdessen ist es dort üblich, die Speicherverwaltung von der C++-Standardbibliothek übernehmen zu lassen, indem man die angebotenen Containerklassen verwendet. Andere Ressourcen, z. B. Dateihandles oder Netzwerksockets werden in C++ üblicherweise in eigenen Klassen mit dem Prinzip RAII verwaltet, um das automatische Aufräumen nach der Verwendung sicherzustellen.

Wenn man selbst in Objekten auf andere Objekte verweist, arbeitet man, als Alternative zu einem Garbage Collector, üblicherweise mit Smart Pointern, die die Ressourcenverwaltung dann übernehmen. Die Standardbibliothek verwendet hier intern meist Reference counting.

Unvollständige Objektorientierung

Unvollständige Kapselung

In C++ sind die Speicherbereiche der einzelnen Objekte zur Laufzeit nicht vor (absichtlichen oder versehentlichen) gegenseitigen Änderungen geschützt.

Undefiniertes Verhalten

Das Verhalten von einigen Sprachkonstrukten ist nicht definiert. Dies bedeutet, dass der Standard weder vorgibt noch empfiehlt, was in einem solchen Fall passiert. Die Auswirkungen reichen von Implementierungsabhängigkeit (d. h. je nach Zielrechner und Compiler kann sich das Konstrukt unterschiedlich verhalten) über unsinnige Ergebnisse oder Programmabstürze bis hin zu Sicherheitslücken. Einige dieser Freiheiten des Compilers lassen zusätzliche Optimierungen des Codes zu.

Es kommt zu unterschiedlichem Verhalten bei

  • verschiedenen Compilern
  • verschiedenen Compiler-Versionen
  • verschiedener Architektur (ARM, x86, x64)
  • verschiedenen Optimierungseinstellungen (Debug, Release, Optimierung)
  • ausgewählter Befehlssatz, Aufrufkonventionen, u. v. a. m.

Quellcode mit Codepassagen mit undefiniertem Verhalten kann nach der Kompilierung unerwartetes und absurd erscheinenden Verhalten zeigen. So werden zu spät durchgeführte Überprüfungen wegoptimiert oder Schleifen, die auf einen ungültigen Index eines Arrays zugreifen, durch leere Endlosschleifen ersetzt.

Wichtig für das Verständnis von undefiniertem Verhalten ist insbesondere, dass niemals nur eine einzelne Operation ungültig ist, sondern das gesamte Programm ungültig wird und kein wohlgeformtes C++ mehr darstellt. Der Grund ist, dass manche Arten von „undefiniertem Verhalten“ Auswirkungen auf ganz andere, auch in sich korrekte, Programmteile haben und deren Verhalten beeinflussen können, beispielsweise bei Pufferüberläufen oder der unbeabsichtigten Änderung von Prozessor-Flags, die durch eine ungültige arithmetische Operation verursacht wurde und die nachfolgenden Berechnungen beeinflussen kann.

Beispiele für undefiniertes Verhalten:

  • Überlauf von vorzeichenbehafteten Ganzzahlen (auch z. B. bei Umwandlung von unsigned int nach int)
  • Nullzeiger-Dereferenzierungen
  • Arrayzugriffe mit ungültigem Index
  • Schiebeoperationen mit einer Schiebeweite, die negativ oder größergleich der Zahl der Bits des zu schiebenden Typs ist
  • Division durch null mit integralen Datentypen
  • Weglassen des return-Statements in Funktionen mit Rückgabewert (die Hauptfunktion main bildet die einzige Ausnahme)
  • Ein Nebeneffekt ändert eine Variable, die mehrmals in dem Ausdruck (v[i] = i++;) oder in der Argumentliste (f(i, i++);) vorkommt (die Auswertungsreihenfolge von Teilausdrücken und Funktionsargumenten ist nicht festgelegt)

Einerseits ist das hieraus resultierende nichtdeterministische Laufzeitverhalten, insbesondere bei kleinen Änderungen der Plattform, mindestens als Risiko, in der Praxis oft aber als klarer Nachteil einzustufen. Andererseits werden hierdurch schnellere Programme ermöglicht, da Gültigkeitsüberprüfungen weggelassen werden können und der Compiler zudem oft Programmteile stärker optimieren kann, indem er Randfälle als per Definition ausgeschlossen ignoriert.

Ein oft nicht wahrgenommener Vorteil ist darüber hinaus, dass dadurch, dass undefiniertes Verhalten praktisch nur in äußerst fragwürdigen Konstrukten auftritt, die aber nicht zwingend während des Kompilierens feststellbar sind, unsemantischer oder anderweitig suboptimaler Code gewissermaßen verboten wird.

Beispielsweise besteht eine illegale Art zu prüfen, ob die Summe zweier positiver Ganzzahlen und vom Typ ‚int‘ verlustfrei wieder in einem ‚int‘ abgebildet werden kann, daraus, zu schauen, ob ihre Summe größer 0 ist (bei Überlauf entsteht auf den meisten Computern durch die Zweierkomplement-Arithmetik eine negative Zahl). Eine derartige Überprüfung ist allerdings aus mathematischer Sicht nicht besonders sinnvoll. Eine bessere (semantischere) Herangehensweise ist hier, die eigentliche Frage, ob , wobei die größte in einem ‚int‘ darstellbare Zahl ist, nach der mathematisch validen Umformung zu zu verwenden.

Programmbeispiel

Der folgende Quelltext ist ein einfaches C++-Programm, das den Text „Hallo Welt!“ in den Standardausgabestrom, üblicherweise das Terminal, schreibt:

#include <iostream> <span title="Aus: Deutsche Wikipedia, Abschnitt &quot;Programmbeispiel&quot;" class="plainlinks">[https://de.wikipedia.org/wiki/C++#Programmbeispiel <span style="color:#dddddd">ⓘ</span>]</span>

int main() {
    std::cout << "Hallo Welt!" << std::endl;
    return 0;
}

Der Präprozessorbefehl oder auch Präprozessordirektive genannt #include bindet Header-Dateien ein, die typischerweise Deklarationen von Variablen, Typen und Funktionen enthalten. Im Gegensatz zu C besitzen Header der C++-Standardbibliothek keine Dateiendung.

Der Header <iostream> ist Teil der C++-Standardbibliothek und deklariert unter anderem den Standardeingabestrom std::cin und die Standardausgabeströme std::cout und std::cerr für die aus der C-Standardbibliothek bekannten Objekte stdin, stdout und stderr.

Bei main() handelt es sich um die Funktion, die den Einsprungspunkt jedes C++-Programms darstellt. Das Programm wird ausgeführt, indem die Funktion main() aufgerufen wird, wobei diese ihrerseits andere Funktionen aufrufen kann. Die Funktion main() selbst darf allerdings in einem C++-Programm nicht rekursiv aufgerufen werden.

Der Standard verlangt von Implementierungen, zwei Signaturen für die Funktion main() zu unterstützen: Eine ohne Funktionsparameter wie im Beispiel, und eine, die einen Integer und einen Zeiger auf Zeiger auf char entgegennimmt, um auf Kommandozeilenparameter zugreifen zu können (was nicht in allen Programmen vonnöten ist): int main(int argc, char **argv). Implementierungen dürfen darüber hinaus weitere Signaturen für main() unterstützen, alle müssen jedoch den Rückgabetyp int (Integer) besitzen, also eine Ganzzahl zurückgeben. Würde main() keinen Wert zurückgeben, schreibt der C++-Standard der Implementierung vor, return 0; anzunehmen. main() gibt also 0 zurück, wenn kein anderslautendes return-Statement in ihr vorhanden ist.

std::cout ist eine Instanz der Klasse std::basic_ostream<char>, die sich wie die gesamte C++-Standardbibliothek im Namensraum std befindet. Bezeichner in Namensräumen werden mit dem Bereichsoperator (::) angesprochen.

Die Ausgabe des Zeichenkettenliterals "Hallo Welt" übernimmt der Operator <<. Zeichenkettenliterale sind in C++ vom Typ Array aus N konstanten chars (char const[N]), wobei N gleich der Länge der Zeichenkette + 1 für die abschließende Nullterminierung ist. Da die Standardtypumwandlungen von C++ die als pointer-to-array decay bekannte implizite Umwandlung eines Arrays T[N] in einen Pointer T* vorsehen, und damit char const[N] in einen char const* zerfällt, passt der überladene Operator template<class traits> basic_ostream<char,traits>& operator<<(std::basic_ostream<char,traits>&, char const *); aus <ostream> und wird entsprechend aufgerufen (operator<<( std::cout, "Hallo Welt!" );) und gibt die Zeichenkette aus. Durch den Ausgabemanipulator std::endl wird ein Zeilenendezeichen ausgegeben.

Bei return 0 wird dem aufrufenden Programm über das Betriebssystem mitgeteilt, dass die Ausführung des Programms erfolgreich war.

Dateiendungen

Typische Dateiendungen sind .C, .cc, .cpp, .cxx, .c++, .h, .hh, .hpp, .hxx, .h++, .ipp, .tpp.

Integrierte Entwicklungsumgebungen

Freie Entwicklungsumgebungen

  • Anjuta
  • Arduino
  • CodeLite
  • Code::Blocks
  • Eclipse
  • Geany
  • GNAT Programming Studio
  • KDevelop
  • MonoDevelop
  • NetBeans IDE
  • Orwell Dev-C++
  • Qt Creator
  • TOPCASED
  • Ultimate++
  • WideStudio

Proprietäre Entwicklungsumgebungen

  • C++Builder
  • CLion (basiert auf IntelliJ IDEA)
  • CodeWarrior
  • Conzept 16 (Programmiersprache C++ ähnlich)
  • Cubic IDE
  • ICON-L
  • Kylix
  • Visual Studio
  • Xcode

Vergleich mit anderen Sprachen

Objective-C

C++ war nicht der einzige Ansatz, die Programmiersprache C um Eigenschaften zu erweitern, die das objektorientierte Programmieren vereinfachen. In den 1980er Jahren entstand die Programmiersprache Objective-C, die sich aber im Gegensatz zu C++ syntaktisch wie von ihrem Funktionsprinzip an Smalltalk und nicht an Simula orientierte. Die Syntax von Objective-C (C beeinflusst durch Smalltalk) unterscheidet sich erheblich von C++ (C beeinflusst von Simula mit ganz eigenen syntaktischen Erweiterungen). Ende der 1980er Jahre wurde Objective-C erstmals kommerziell in NeXTStep verwendet, in dem es einen zentralen Bestandteil darstellt. Heutzutage findet es in der Programmierschnittstelle OpenStep (bzw. Cocoa und GNUstep) sowie in den Betriebssystemen iOS und macOS ein wichtiges Einsatzgebiet.

Java und C#

Die Programmiersprachen Java und C# verfügen über eine ähnliche, ebenfalls an C angelehnte Syntax wie C++, sind auch objektorientiert und unterstützen seit einiger Zeit Typparameter. Trotz äußerlicher Ähnlichkeiten unterscheiden sie sich aber konzeptionell von C++ zum Teil beträchtlich.

Generische Techniken ergänzen die objektorientierte Programmierung um Typparameter und erhöhen so die Wiederverwertbarkeit einmal kodierter Algorithmen. Die generischen Java-Erweiterungen sind jedoch lediglich auf Klassen, nicht aber auf primitive Typen oder Datenkonstanten anwendbar. Demgegenüber beziehen die generischen Spracherweiterungen von C# auch die primitiven Typen mit ein. Dabei handelt es sich allerdings um eine Erweiterung für Generik zur Laufzeit, die die auf Kompilationszeit zugeschnittenen C++-Templates zwar sinnvoll ergänzen, nicht aber ersetzen können.

Gerade die generische Programmierung macht C++ zu einem mächtigen Programmierwerkzeug. Während die objektorientierte Programmierung in Java und C# nach wie vor den zentralen Abstraktionsmechanismus darstellt, ist diese Art der Programmierung in C++ rückläufig. So werden tiefe Klassenhierarchien vermieden, und zu Gunsten der Effizienz und der Minimierung des Ressourcenverbrauchs verzichtet man in vielen Fällen auf Polymorphie, einen der fundamentalen Bestandteile der objektorientierten Programmierung.

Entstehung und Weiterentwicklung

Weiterentwicklung der Programmiersprache C++ nach 2005

Um mit den aktuellen Entwicklungen der sich schnell verändernden Computer-Technik Schritt zu halten, aber auch zur Ausbesserung bekannter Schwächen, erarbeitete das C++-Standardisierungskomitee die nächste größere Revision von C++, die inoffiziell mit C++0x abgekürzt wurde, worin die Ziffernfolge eine grobe Einschätzung des möglichen Erscheinungstermins andeuten sollte. Später, als ein Erscheinungstermin bis Ende 2009 nicht mehr zu halten war, änderte sich der inoffizielle Name zu C++1x.

Die vorrangigen Ziele für die Weiterentwicklung von C++ waren Verbesserungen im Hinblick auf die Systemprogrammierung sowie zur Erstellung von Programmbibliotheken. Außerdem sollte die Erlernbarkeit der Sprache für Anfänger verbessert werden.

Im November 2006 wurde der Zieltermin für die Fertigstellung auf das Jahr 2009 festgelegt. Im Juli 2009 wurde dieser Termin auf frühestens 2010 geändert. Im August 2011 wurde die Revision einstimmig von der ISO angenommen und am 11. Oktober 2011 als ISO/IEC 14882:2011 offiziell veröffentlicht. Inoffiziell heißt die Version C++11.

Verbesserungen am Sprachkern

C++98 deckte einige typische Problemfelder der Programmierung noch nicht ausreichend ab, zum Beispiel die Unterstützung von Nebenläufigkeit (Threads), deren Integration in C++, insbesondere für die Verwendung in Mehrprozessorumgebungen, eine Überarbeitung der Sprache unumgänglich machte. Durch die Einführung eines Speichermodells wurden Garantien der Sprache für den nebenläufigen Betrieb festgelegt, um Mehrdeutigkeiten in der Abarbeitungsreihenfolge sowohl aufzulösen als auch in bestimmten Fällen aufrechtzuerhalten und dadurch Spielraum für Optimierungen zu schaffen.

Zu den weitreichenderen Spracherweiterungen gehörte ferner die automatische Typableitung zur Ableitung von Ergebnistypen aus Ausdrücken und die sogenannten R-Wert-Referenzen, mit deren Hilfe sich als Ergänzung zu dem bereits vorhandenen Kopieren von Objekten dann auch ein Verschieben realisieren lässt, außerdem bereichsbasierte For-Schleifen (foreach) über Container und eingebaute Felder.

C++11

Mit der Norm ISO/IEC 14882:2011, auch bekannt als C++11, wurden viele weitreichende Neuerungen in C++ eingeführt, wie auszugsweise:

  • Lambdas (Anonyme Funktionen), welche vor der Verabschiedung des C++11-Standards in anderen Sprachen schon teils breite Anwendung fanden, erweitern die Sprache vor allem im Bereich der funktionalen Programmierung.
  • Eine erleichterte Typbehandlung mit Typinferenz ist nun über das Schlüsselwort auto (das nun nicht mehr ein Speicherklassen-Specifier ist) einerseits und das Schlüsselwort decltype (das den Typ eines Ausdrucks statisch zur Compilezeit zurückgibt, sofern ermittelbar) andererseits möglich. Beide Schlüsselworte zeigen ihre Stärke zudem auch im Verbund. So können ganze Funktionen, deren Rückgabetypen beispielsweise nur schwer vom Programmierer einzusehen sind, weil sie beispielsweise innerhalb komplexerer Klassentemplates liegen, komfortabel definiert werden:
    template <typename Factory>
    auto createObject(const Factory& creator) -> decltype(creator.makeObject()) {
        return creator.makeObject();
    }
  • Streng typisierte enums (enum class) beseitigen Probleme mit Namenskollisionen und schränken die Fehleranfälligkeit in Bezug auf implizite Typkonvertierungen ein.
  • Sogenannte „range-based loops“ mittels eines modifizierten for-Statements erleichtern die Arbeit mit Containern und Arrays in der Art, dass Iteratoren beim Traversieren von Objekten dieser Datentypen für viele Anwendungsfälle überflüssig werden:
    #include <iostream>
    #include <string>
    #include <vector> <span title="Aus: Deutsche Wikipedia, Abschnitt &quot;C++11&quot;" class="plainlinks">[https://de.wikipedia.org/wiki/C++#C++11 <span style="color:#dddddd">ⓘ</span>]</span>
    
    using namespace std; <span title="Aus: Deutsche Wikipedia, Abschnitt &quot;C++11&quot;" class="plainlinks">[https://de.wikipedia.org/wiki/C++#C++11 <span style="color:#dddddd">ⓘ</span>]</span>
    
    void printNames(const vector<string>& names) {
        for (const string& singleName: names)
            cout << singleName << endl;
    }
  • Es dürfen direkt aufeinanderfolgende spitze Klammern bei Templates benutzt werden: map<int, vector<int>>.
  • Überdies erfolgte mit der Einführung von „variadic templates“ eine großräumige Erweiterung der Nutzungsmöglichkeiten von Templates. Diese ermöglichen nun eine nicht fixe Anzahl von Template-Argumenten template<typename... Values> class VariadicExampleClass;, was weitreichende Optionen und Vereinfachungen im Code- bzw. Algorithmenaufbau und der Codestruktur allgemein ermöglicht. Des Weiteren haben sie, wie viele andere C++11-Erweiterungen auch, das Potential, die Notwendigkeit zur Nutzung von teils fehleranfälligen und nicht robusten Macros weiter einzuschränken.
  • Die explizite Nutzbarkeit sogenannter Rvalue-Referenzen ermöglicht, aufbauend unter anderem auf sogenannter Bewegungssemantik, ein breites Spektrum von Codevereinfachungen, Laufzeitoptimierungen und ausnahmesicherer Programmierung. Mit den Rvalue-Referenzen wurden auch die sogenannten universellen Referenzen eingeführt, welche das Problem des „Perfect forwarding“ auf Sprachebene robust und einfach lösbar machen (die konsistente Weiterreichung von Typen innerhalb von Templatekonstrukten, die per „type deduction“ aufgelöst wurden, an weiterführende Templates). Vor der Verabschiedung des C++11-Standards war dies zumindest rein auf Sprachebene nicht möglich und erforderte vom Programmierer je nach Problemfall mehr oder weniger viel Eigenregie mit teils entsprechender Codeaufblähung und -Duplizierung.
  • Außerdem wurden einige Features aus C11 übernommen, zum Beispiel Ganzzahlen mit mindestens 64 Bit (long long) oder Zusicherungen zur Übersetzungszeit mittels static_assert (in C11: _Static_assert).

Themen der Sprache C++, die Rechenzeit und Speicherplatz betreffen, wurden im sogenannten technical report ISO/IEC TR 18015:2006 behandelt.

Zum Zeitpunkt der Einführung des Standards und auch noch vergleichsweise lange darüber hinaus unterstützten viele gängige Compiler diesen nicht vollständig bzw. mit Bezug auf einige Erweiterungen mitunter fehlerhaft. Besonders starke Einschränkungen zeigte diesbezüglich zum Beispiel Microsoft mit Visual C++ 2012. Mit Visual C++ 2015 sind mittlerweile jedoch nahezu alle wichtigen größeren Spracherweiterungen berücksichtigt worden.

C++14

C++14, beschrieben im Standard ISO/IEC 14882:2014, erweitert die Einsatzmöglichkeiten von auto und decltype, schwächt die Voraussetzungen für constexpr ab, erlaubt Variablen-Templates zu definieren (beispielsweise um mehrere Versionen von π mit je nach Typ unterschiedlicher Genauigkeit zu definieren), führt Binärliterale ein (0b...), führt Hochkommata als Trennzeichen in Zahlen ein, erlaubt generische Lambdas, erweitert Lambda capture expressions und führt das Attribut deprecated ein.

Außerdem wurde die Standardbibliothek um ein paar Funktionen ergänzt, die bei C++11 „vergessen“ bzw. „übersehen“ wurden (z. B. std::make_unique) und etliche Funktionsdeklarationen nun als constexpr umdeklariert, was dem Compiler aggressivere Optimierungen gestattet.

Während der Entwicklungsphase wurde C++14 auch C++1y genannt, um anzudeuten, dass es die Nachfolgeversion der vormals als C++0x genannten Version sein wird.

C++17

Im März 2017 hat das ISO-C++-Komitee den Sprachstandard C++17 technisch abgeschlossen. Für die neue Fassung wurde unter anderem die Aufnahme des Typen std::byte beschlossen. Dieser ist explizit für den byte-weisen Zugriff auf den Speicher bestimmt. Es wurden neue, generische Container eingeführt: std::any als Alternative zu void* mit Typeüberprüfung zur Laufzeit, std::variant als Alternative zur aus C übernommenen Union mit Laufzeit-Typprüfung und std::optional, ein Container, der genau ein Element enthalten kann, aber nicht muss.

Bis zur offiziellen Verabschiedung wurde die Fassung auch als C++1z bezeichnet.

Nach dem Sommer-Meeting Mitte Juli verriet der C++-Experte Herb Sutter, der für die Einberufung des Komitees verantwortlich ist, in seinem Blog bereits erste Pläne für C++20.

C++20

Die finale Version von C++20 wurde im Dezember 2020 veröffentlicht, nachdem er im Februar finalisiert und im September bestätigt wurde.

  • Concepts
  • bestimmte Initialisierer (zuerst in C99)
  • [=, this] in Lambdas
  • Template-Parameter-Listen in Parametern
  • Dreiwegevergleich mit dem „spaceship operator“, operator <=>
  • Koroutinen
  • ein Modulsystem zur Codekapselung und kürzeren Kompilierzeiten
  • std::format als moderner Ersatz für sprintf und stringstreams
  • std::span Erweiterung des Konzeptes von string_view (C++17) auf beliebige Felder

C++23/26

  • Reflection
  • Executors
  • Pattern Matching
  • Networking