Alle bereits im Kapitel „C-Sprachunterstützung des Compilers“ (Implementierungsabhängiges Verhalten gemäß dem ANSI-/ISO-C-Standard) beschriebenen Implementierungsabhängigkeiten gemäß dem ANSI-/ISO-C-Standard treffen auch in den C++-Sprachmodi zu und werden in diesem Abschnitt nicht mehr aufgeführt.
Im Folgenden werden nur die über die Sprache C hinausgehenden implementierungsabhängigen C++-Spracheigenschaften beschrieben. Welche C++-Spracheigenschaften in welchen C++ Modi unterstützt werden, können Sie der Übersichtstabelle im Abschnitt "Die C++-Sprachmodi im Überblick" entnehmen.
Bezeichner mit externer Bindung
Übersetzung im Cfront-C++-Modus
Externe C++-Namen werden vom Compiler auf 32 Zeichen verkürzt. Bei der Generierung von gemeinsam benutzbarem Code (Optionen-K share
bzw. SHAREABLE-CODE=*YES) können nur maximal 30 Zeichen genutzt werden. Standardmäßig werden Kleinbuchstaben in Großbuchstaben umgewandelt. Durch die Angabe der Option-K llm_case_lower
bzw. LOWER-CASE-NAMES=*YES können Kleinbuchstaben in den externen Namen beibehalten werden.Übersetzung in den Modi C++ V3, C++ 2017 bzw. C++ 2020
Für die Bildung von externen C++-Namen sind alle Zeichen signifikant. Es findet keine Namensverkürzung und keine Umwandlung von Klein- in Großbuchstaben statt. Der Compiler generiert Entry-Namen im EEN-Format, die eine Länge von maximal 32000 Zeichen erreichen können. Längere Namen führen zu einem Fehler.
Externe C++-Namen werden für den Binder codiert, so dass sie nur noch erlaubte Zeichen enthalten und eindeutig sind (name mangling). Diese interne Namenscodierung erfolgt im Cfront-C++-Modus anders als in den anderen C++-Modi. Mit der Option -N project
bzw. PROJECT-INFORMATION=*YES erhält man eine Liste mit einer Gegenüberstellung aller im Quellprogramm original verwendeten externen C++-Namen und den Namen, die der Compiler für den Binder intern generiert.
Linkage der main-Funktion
Die Funktion main
hat externe C-Linkage.
Datentyp bool
Der Typ bool
hat die Größe sizeof(bool) == 1
.
reinterpret_cast
Das Zielobjekt enthält dasselbe Bitmuster wie der Ausdruck, der durch reinterpret_cast
konvertiert wurde. Aber das Zielobjekt ist möglicherweise kein gültiges Objekt (z.B. reinterpret_cast<float>(int)
) des gewünschten Datentyps.
Folgende Konversionen sind möglich:
Ein Zeiger kann explizit in einen Integer-Typ konvertiert werden, der groß genug dafür ist. In Abhängigkeit davon, ob der Zieltyp
signed
oderunsigned
ist, kann der Ergebniswert der Konversion vorzeichenbehaftet sein.Der Wert eines Integer-Typs kann explizit in einen Zeiger konvertiert werden.
Array-new
Für jedes allokierte Array wird am Beginn des für das Array allokierten Speicherblocks eine Struktur reserviert. Diese Struktur enthält zwei size_t
-Elemente, eine für die Größe des Arrays in Bytes und eine für die Anzahl der Array-Elemente (dieses Feld wird verschlüsselt, um erkennen zu können, ob die Struktur überschrieben worden ist).
Schlüsselwort asm
Während das Schlüsselwort asm
gegenüber dem ANSI-/ISO-C-Standard eine Erweiterung darstellt, ist es im ANSI-C++-Standard als reserviertes Schlüsselwort definiert. Da jedoch die Generierung von Inline-Assemblercode nicht unterstützt wird, führt der Gebrauch zu einer Fehlermeldung.
Linkage-Spezifizierer
Es werden die Linkage-Spezifizierer "C++" und "C" unterstützt.
Der voreingestellte Linkage-Spezifizierer ist "C++". Namen mit externer C++-Linkage werden vom Compiler intern umgewandelt, damit sie vom Binder verarbeitet werden können (name mangling). Diese interne Namenscodierung erfolgt in den verschiedenen C++-Modi unterschiedlich.
Bei Namen mit externer C-Linkage wird keine interne Namenscodierung durchgeführt. C-Linkage kann benutzt werden, um Funktionen zu verknüpfen und aufzurufen, die in C oder in einer Sprache geschrieben sind, die sich an ihrer Namenscodierungs-Schnittstelle wie C verhält.
Ein Zeiger auf eine C-Funktion ist kompatibel mit einem Zeiger auf eine C++-Funktion desselben Typs, wenn die Argumenttypen C-kompatible PODs sind. In C++ 2017 sind dies zwei verschiedene Typen. Im erweiterten C++ 2017-Modus gibt es eine implizite Konversion zwischen zwei Funktionstypen, die sich nur in der Linkage unterscheiden.
Linkage von Templates
Es werden nur Templates mit C++-Linkage unterstützt.
Template-Instanziierung
siehe "Template-Instanziierung"
Referenztypen
Die Referenz für ein Rvalue ist an ein vom Compiler erzeugtes, temporäres Objekt gebunden. Intern wird der C++-Datentyp Referenz behandelt wie ein Zeiger (siehe "Implementierungsabhängiges Verhalten gemäß dem ANSI-/ISO-C-Standard").
Allokierung von nicht-statischen Datenelementen einer Klasse
Der Speicher für die Datenelemente wird in der strikten Reihenfolge ihrer Deklarationen allokiert, d.h. unabhängig von den Zugriffs-Spezifizierern public
, private
oder protected
.
Bitfelder innerhalb von Klassen
Bitfelder innerhalb von Klassen werden behandelt wie Bitfelder innerhalb von Strukturen. Dies betrifft die Allokierung, die Ausrichtung sowie die Behandlung von „einfachen“ Bitfeldern ohne das Attribut signed
bzw. unsigned
.
Konstruktoren und Destruktoren für globale und lokale statische Objekte
C++ unterstützt die Initialisierung und Finalisierung von Objekten mit Konstruktoren und Destruktoren. Konstruktor- und Destruktoraufrufe können auf dynamische sowie auf globale und lokale statische Objekte angewendet werden.
Konstruktoren und Destruktoren für dynamische Objekte
Konstruktoren für dynamische Objekte werden aufgerufen, wenn das Objekt mit dem new
-Operator erzeugt wird, beim Eintritt in Funktionen oder lokale Blöcke, beim Aufbau von Aktualparametern oder wenn temporäre Objekte erzeugt werden.
Destruktoren für dynamische Objekte werden in umgekehrter Reihenfolge ihrer Konstruktion aufgerufen, z.B. bei Funktionsende (return
, exit
), Blockende usw.
Werden für eine Expression temporäre Objekte angelegt, so werden die Destruktoren dafür am Ende der Expression aufgerufen. Dies gilt insbesondere auch für Parameter von Funktionen.
Konstruktoren und Destruktoren für globale und lokale statische Objekte
Die Art und Reihenfolge der Konstruktor- und Destruktoraufrufe für globale und lokale statische Objekte ist implementierungsabhängig. Technisch gesehen bedeutet dies: Die Konstruktoraufrufe werden pro Übersetzungseinheit in eine Funktion gebündelt. Die Adresse dieser Funktionen wird in einer speziell markierten Variablen im Datenteil des Programms abgelegt. Bei der Initialisierung des Laufzeitsystems wird eine Liste dieser Variablen erstellt. Vor Aufruf der main
-Funktion werden die Konstruktoren aufgerufen. Der Destruktoraufruf erfolgt bei jeder normalen Programmbeendigung, d.h. bei Aufruf von exit
, _exit
, bs2exit
oder return
aus der main
-Funktion, nicht jedoch bei Aufruf von abort
.
Auf der Benutzerseite sind folgende Punkte zu beachten, wenn globale und lokale statische Objekte mit Konstruktoren und Destruktoren verwendet werden:
Das Umlenken der Standard-Ein-/Ausgabe-Dateien (siehe "Umlenken der Standard-Ein-/Ausgabedateien") ist innerhalb der globalen und lokalen statischen Objekte nicht wirksam.
Die Reihenfolge der Konstruktoraufrufe ist absolut undefiniert. Sie kann sogar bei Ablauf des gleichen Programms unterschiedlich sein. Es ist streng darauf zu achten, dass keine Abhängigkeiten zwischen den einzelnen Konstruktoraufrufen existieren.
Die Namen der speziellen Variablen für die Konstruktor-Funktionsadressen (s.o.) beginnen mit ICP. Dieses Präfix sowie generell der Anfangs-Buchstabe
I
darf nicht für die Bildung von benutzereigenen externen Namen verwendet werden, da sonst die Konstruktorbehandlung fehlerhaft arbeitet.Beim Binden darf die ESD-Information nicht unterdrückt werden.
Die Daten des Programms müssen im Klasse-6-Speicher stehen.
Bei gemeinsam benutzbarem Code werden alle Code-Teile nachgeladen, um die Konstruktoren aufzurufen.
Innerhalb eines Destruktors darf keine Sprachverknüpfung stattfinden (nur Fremdsprachenverknüpfung).
Es darf kein Code „entladen“ werden, da sonst bei Programmende Destruktoren aufgerufen würden, für die kein Code mehr vorhanden ist.
Bei echten Subsystemen, für die Daten und Code durch einen expliziten Aufruf nachgeladen werden, muss das Laufzeitsystem erneut initialisiert werden. Dabei wird die Tabelle der Konstruktoren ergänzt, und die neuen Konstruktoren werden aufgerufen.
Ausnahmebehandlung
Ausnahmen auslösen (throw)
Der Speicherplatz für Ausnahmeobjekte wird aus einem vorallokierten Speicher genommen, der ggf. durch malloc
-Aufrufe erweitert wird.
Ausnahmen behandeln
In manchen Situationen wird implizit die Funktion terminate()
aufgerufen. Der C++-Standard lässt in zwei Situationen die Frage offen, ob Destruktoren für automatische Objekte aufgerufen werden:
Wenn kein passender Ausnahme-Handler gefunden wird, werden die Destruktoren nicht aufgerufen.
Wenn auf dem Stack eine Funktion mit der Angabe noexcept(true)
steht, wird nur ein Teil der Destruktoren aufgerufen.
Diese Destruktoren könnten mit der Funktion unwind_exit()
, (siehe "Zusätzliche Laufzeitfunktionen") ausgeführt werden.
Funktion unexpected()
Für das ausgelöste Ausnahmeobjekt, das die Ursache für eine abgebrochene unexpected()
-Funktion ist, wird ein Destruktor aufgerufen. Es wird durch ein neues bad_exception
-Objekt ersetzt.
Weitere Einzelheiten zur Ausnahmebehandlung siehe "Ausnahmebehandlung".
Rückgabetyp von operator->
Der Rückgabetyp von operator->()
wird nicht mehr an dem Punkt geprüft, an dem die Funktion deklariert wird, sondern ausschließlich an dem Punkt, an dem sie als „->“-Operator verwendet wird. (Diese Prüfung wurde bis Version V3.0C nur für Elemente von Klassen-Templates spät durchgeführt.)
Klassen und Ellipsis
Funktionen können mit variablen Argumentlisten deklariert werden (z.B. int foo(int, ...);
). Ein Objekt einer Klasse kann ein Parameter sein, wenn das passende Argument die Ellipsis ist. In diesem Fall wird das Objekt Byte-weise kopiert. Ein evtl. vorhandener Konstruktor wird dabei ignoriert. Ein evtl. vorhandener Destruktur wird für den Parameter nicht aufgerufen.