Mehrere Gründe
Header-Dateien
Für jede einzelne Kompilierungseinheit müssen Hunderte oder sogar Tausende von Headern (1) geladen und (2) kompiliert werden. Jeder von ihnen hat in der Regel für jede Übersetzungseinheit neu kompiliert werden, da die Prä - Prozessor sorgt dafür , dass das Ergebnis eine Kopfzusammenstellung kann zwischen jeder Übersetzungseinheit variieren. (Ein Makro kann in einer Kompilierungseinheit definiert werden, die den Inhalt des Headers ändert.)
Dies ist wahrscheinlich der Hauptgrund, da für jede Kompilierungseinheit große Mengen an Code kompiliert werden müssen und außerdem jeder Header mehrmals kompiliert werden muss (einmal für jede Kompilierungseinheit, die ihn enthält).
Verknüpfen
Nach dem Kompilieren müssen alle Objektdateien miteinander verknüpft werden. Dies ist im Grunde ein monolithischer Prozess, der nicht sehr gut parallelisiert werden kann und Ihr gesamtes Projekt verarbeiten muss.
Parsing
Die Syntax ist äußerst kompliziert zu analysieren, hängt stark vom Kontext ab und ist sehr schwer zu unterscheiden. Das braucht viel Zeit.
Vorlagen
In C # List<T>
ist der einzige Typ, der kompiliert wird, unabhängig davon, wie viele Instanzen von List Sie in Ihrem Programm haben. In C ++ vector<int>
ist ein völlig separater Typ von vector<float>
, und jeder muss separat kompiliert werden.
Hinzu kommt, dass Vorlagen eine vollständige Turing-vollständige "Subsprache" bilden, die der Compiler interpretieren muss, und dies kann lächerlich kompliziert werden. Selbst relativ einfacher Metaprogrammiercode für Vorlagen kann rekursive Vorlagen definieren, die Dutzende und Dutzende von Vorlageninstanziierungen erstellen. Vorlagen können auch zu äußerst komplexen Typen mit lächerlich langen Namen führen, die dem Linker viel zusätzliche Arbeit hinzufügen. (Es müssen viele Symbolnamen verglichen werden, und wenn diese Namen zu vielen tausend Zeichen werden können, kann dies ziemlich teuer werden.)
Und natürlich verschärfen sie die Probleme mit Header-Dateien, da Vorlagen im Allgemeinen in Headern definiert werden müssen, was bedeutet, dass für jede Kompilierungseinheit viel mehr Code analysiert und kompiliert werden muss. Im einfachen C-Code enthält ein Header normalerweise nur Vorwärtsdeklarationen, aber nur sehr wenig tatsächlichen Code. In C ++ ist es nicht ungewöhnlich, dass sich fast der gesamte Code in Header-Dateien befindet.
Optimierung
C ++ ermöglicht einige sehr dramatische Optimierungen. Mit C # oder Java können Klassen nicht vollständig eliminiert werden (sie müssen zu Reflexionszwecken vorhanden sein), aber selbst ein einfaches C ++ - Vorlagen-Metaprogramm kann leicht Dutzende oder Hunderte von Klassen generieren, die alle in der Optimierung inline und wieder eliminiert werden Phase.
Darüber hinaus muss ein C ++ - Programm vom Compiler vollständig optimiert werden. Das AC # -Programm kann sich darauf verlassen, dass der JIT-Compiler beim Laden zusätzliche Optimierungen durchführt. C ++ erhält keine solchen "zweiten Chancen". Was der Compiler generiert, ist so optimiert wie es nur geht.
Maschine
C ++ wird zu Maschinencode kompiliert, der möglicherweise etwas komplizierter ist als der von Java oder .NET verwendete Bytecode (insbesondere im Fall von x86). (Dies wird nur der Vollständigkeit halber erwähnt, weil es in Kommentaren und dergleichen erwähnt wurde. In der Praxis ist es unwahrscheinlich, dass dieser Schritt mehr als einen winzigen Bruchteil der gesamten Kompilierungszeit in Anspruch nimmt.)
Fazit
Die meisten dieser Faktoren werden von C-Code gemeinsam genutzt, der tatsächlich ziemlich effizient kompiliert wird. Der Parsing-Schritt ist in C ++ viel komplizierter und kann erheblich mehr Zeit in Anspruch nehmen, aber der Haupttäter sind wahrscheinlich Vorlagen. Sie sind nützlich und machen C ++ zu einer weitaus leistungsfähigeren Sprache, aber sie fordern auch ihren Tribut in Bezug auf die Kompilierungsgeschwindigkeit.