Betrachten wir die folgenden Hallo-Welt-Beispiele in C und C ++:
#include <stdio.h>
int main()
{
printf("Hello world\n");
return 0;
}
#include <iostream>
int main()
{
std::cout<<"Hello world"<<std::endl;
return 0;
}
Wenn ich sie in Godbolt to Assembly kompiliere, beträgt die Größe des C-Codes nur 9 Zeilen ( gcc -O3
):
.LC0:
.string "Hello world"
main:
sub rsp, 8
mov edi, OFFSET FLAT:.LC0
call puts
xor eax, eax
add rsp, 8
ret
Die Größe des C ++ - Codes beträgt jedoch 22 Zeilen ( g++ -O3
):
.LC0:
.string "Hello world"
main:
sub rsp, 8
mov edx, 11
mov esi, OFFSET FLAT:.LC0
mov edi, OFFSET FLAT:_ZSt4cout
call std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long)
mov edi, OFFSET FLAT:_ZSt4cout
call std::basic_ostream<char, std::char_traits<char> >& std::endl<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&)
xor eax, eax
add rsp, 8
ret
_GLOBAL__sub_I_main:
sub rsp, 8
mov edi, OFFSET FLAT:_ZStL8__ioinit
call std::ios_base::Init::Init() [complete object constructor]
mov edx, OFFSET FLAT:__dso_handle
mov esi, OFFSET FLAT:_ZStL8__ioinit
mov edi, OFFSET FLAT:_ZNSt8ios_base4InitD1Ev
add rsp, 8
jmp __cxa_atexit
... was viel größer ist.
Es ist berühmt, dass Sie in C ++ für das bezahlen, was Sie essen. Wofür bezahle ich in diesem Fall?
eat
mit C ++ verbundenen Begriff noch nie gehört . Ich glaube du meinst: "Du bezahlst nur für das, was du benutzt "?eat
mehrdeutiger ist und vermieden werden sollte.Antworten:
Was Sie bezahlen, ist, eine schwere Bibliothek aufzurufen (nicht so schwer wie das Drucken in die Konsole). Sie initialisieren ein
ostream
Objekt. Es gibt einige versteckte Speicher. Dann rufen Sie an,std::endl
was kein Synonym für ist\n
. Dieiostream
Bibliothek hilft Ihnen dabei, viele Einstellungen anzupassen und den Prozessor und nicht den Programmierer zu belasten. Dafür bezahlen Sie.Lassen Sie uns den Code überprüfen:
Initialisieren eines Ostream-Objekts + Cout
Rufen Sie
cout
erneut an, um eine neue Zeile zu drucken und zu spülenStatische Speicherinitialisierung:
Es ist auch wichtig, zwischen der Sprache und der Bibliothek zu unterscheiden.
Übrigens ist dies nur ein Teil der Geschichte. Sie wissen nicht, was in den von Ihnen aufgerufenen Funktionen geschrieben steht.
quelle
cout; printf; cout
Schreibvorgänge in der richtigen Reihenfolge ausgeführt werden (da sie über eigene Puffer verfügen). Der zweite wird desynchronisiertcout
undcin
führt dazucout; cin
, dass der Benutzer möglicherweise zuerst nach Informationen gefragt wird. Durch das Spülen wird die Synchronisierung nur dann erzwungen, wenn Sie dies tatsächlich benötigen.std::cout
ist mächtiger und komplizierter alsprintf
. Es unterstützt Dinge wie Gebietsschemas, Stateful-Formatierungsflags und mehr.Wenn Sie diese nicht benötigen, verwenden Sie
std::printf
oderstd::puts
- sie sind in verfügbar<cstdio>
.Ich möchte auch klarstellen, dass C ++ ! = Die C ++ - Standardbibliothek. Die Standardbibliothek soll universell einsetzbar und "schnell genug" sein, ist jedoch häufig langsamer als eine spezielle Implementierung Ihrer Anforderungen.
Andererseits ist die C ++ - Sprache bestrebt, das Schreiben von Code zu ermöglichen, ohne unnötige zusätzliche versteckte Kosten zu zahlen (z. B. Opt-In
virtual
, keine Speicherbereinigung).quelle
Sie vergleichen C und C ++ nicht. Sie vergleichen
printf
undstd::cout
, die zu verschiedenen Dingen fähig sind (Gebietsschemas, zustandsbehaftete Formatierung usw.).Versuchen Sie, den folgenden Code zum Vergleich zu verwenden. Godbolt generiert für beide Dateien dieselbe Assembly (getestet mit gcc 8.2, -O3).
Haupt c:
main.cpp:
quelle
Ihre Angebote vergleichen zwar Äpfel und Orangen, aber nicht aus dem Grund, der in den meisten anderen Antworten impliziert ist.
Lassen Sie uns überprüfen, was Ihr Code tatsächlich tut:
C:
"Hello world\n"
C ++:
"Hello world"
instd::cout
std::endl
Manipulator instd::cout
Anscheinend macht Ihr C ++ - Code doppelt so viel Arbeit. Für einen fairen Vergleich sollten wir dies kombinieren:
… Und plötzlich
main
sieht Ihr Assembler-Code für C sehr ähnlich aus:Tatsächlich können wir den C- und C ++ - Code Zeile für Zeile vergleichen, und es gibt nur sehr wenige Unterschiede :
Der einzige wirkliche Unterschied besteht darin, dass wir in C ++
operator <<
mit zwei Argumenten (std::cout
und der Zeichenfolge) aufrufen . Wir könnten sogar diesen kleinen Unterschied beseitigen, indem wir ein näheres Äquivalent verwenden:,fprintf
das auch ein erstes Argument enthält, das den Strom spezifiziert.Dadurch bleibt der Assemblycode für
_GLOBAL__sub_I_main
übrig, der für C ++, aber nicht für C generiert wird. Dies ist der einzige echte Overhead, der in dieser Assemblyliste angezeigt wird (es gibt natürlich mehr unsichtbaren Overhead für beide Sprachen). Dieser Code führt zu Beginn des C ++ - Programms eine einmalige Einrichtung einiger C ++ - Standardbibliotheksfunktionen durch.Wie in anderen Antworten erläutert, wird der relevante Unterschied zwischen diesen beiden Programmen jedoch nicht in der Assembly-Ausgabe der
main
Funktion gefunden, da das ganze schwere Heben hinter den Kulissen stattfindet.quelle
_start
aber sein Code ist Teil der C - Laufzeitbibliothek. In jedem Fall geschieht dies sowohl für C als auch für C ++.std::cout
und übergibt stattdessen E / A an die stdio-Implementierung (die ihre eigenen Puffermechanismen verwendet). Insbesondere wenn eine Verbindung zu einem interaktiven Terminal besteht (was als solches bekannt ist), wird beim Schreiben standardmäßig nie eine vollständig gepufferte Ausgabe angezeigtstd::cout
. Sie müssen die Synchronisation mit stdio explizit deaktivieren, wenn die iostream-Bibliothek ihre eigenen Puffermechanismen für verwenden sollstd::cout
.printf
müssen die Streams hier nicht gespült werden. In einem allgemeinen Anwendungsfall (Ausgabe in Datei umgeleitet) werden Sie normalerweise feststellen, dass dieseprintf
Anweisung nicht gelöscht wird. Nur wenn der Ausgang zeilengepuffert oder ungepuffert ist,printf
löst der Trigger einen Flush aus.Das ist einfach. Sie bezahlen für
std::cout
. "Sie bezahlen nur für das, was Sie essen" bedeutet nicht "Sie bekommen immer die besten Preise". Klarprintf
ist billiger. Man kann argumentieren, dass diesstd::cout
sicherer und vielseitiger ist, weshalb die höheren Kosten gerechtfertigt sind (es kostet mehr, bietet aber mehr Wert), aber das geht am eigentlichen Punkt vorbei. Sie verwenden nichtprintf
, Sie verwendenstd::cout
, also zahlen Sie für die Verwendungstd::cout
. Sie zahlen nicht für die Verwendungprintf
.Ein gutes Beispiel sind virtuelle Funktionen. Virtuelle Funktionen haben einige Laufzeitkosten und Platzanforderungen - aber nur, wenn Sie sie tatsächlich verwenden. Wenn Sie keine virtuellen Funktionen verwenden, zahlen Sie nichts.
Ein paar Bemerkungen
Selbst wenn C ++ - Code mehr Assembly-Anweisungen ergibt, handelt es sich immer noch um eine Handvoll Anweisungen, und der Leistungsaufwand wird wahrscheinlich durch tatsächliche E / A-Vorgänge in den Schatten gestellt.
Tatsächlich ist es manchmal sogar besser als "In C ++ zahlen Sie für das, was Sie essen". Beispielsweise kann der Compiler ableiten, dass ein virtueller Funktionsaufruf unter bestimmten Umständen nicht erforderlich ist, und diesen in einen nicht virtuellen Aufruf umwandeln. Das bedeutet, dass Sie möglicherweise virtuelle Funktionen kostenlos erhalten . Ist das nicht toll?
quelle
Die "Assembly-Liste für printf" ist NICHT für printf, sondern für Puts (Art der Compiler-Optimierung?); printf ist viel komplexer als Puts ... nicht vergessen!
quelle
std::cout
die Einbauten hängen bleiben, die in der Baugruppenliste nicht sichtbar sind.puts
, der mit einem Aufruf von identisch ist,printf
wenn Sie nur eine einzelne Formatzeichenfolge und null zusätzliche Argumente übergeben. (außer es wird auch eine geben,xor %eax,%eax
weil wir null FP-Argumente in Registern an eine variable Funktion übergeben.) Keine dieser Implementierungen ist nur die Übergabe eines Zeigers auf eine Zeichenfolge an die Bibliotheksfunktion. Aber ja, die Optimierungprintf
aufputs
ist etwas, was gcc für Formate tut, die nur haben"%s"
oder wenn es keine Konvertierungen gibt, und die Zeichenfolge endet mit einer neuen Zeile.Ich sehe hier einige gültige Antworten, aber ich werde ein bisschen mehr ins Detail gehen.
Wechseln Sie zur folgenden Zusammenfassung, um die Antwort auf Ihre Hauptfrage zu erhalten, wenn Sie nicht die gesamte Textwand durchgehen möchten.
Abstraktion
Sie bezahlen für die Abstraktion . Die Möglichkeit, einfacheren und menschlicheren Code zu schreiben, ist mit Kosten verbunden. In C ++, einer objektorientierten Sprache, ist fast alles ein Objekt. Wenn Sie ein Objekt verwenden, passieren immer drei Dinge unter der Haube:
init()
Methode). Normalerweise erfolgt die Speicherzuweisung als erstes in diesem Schritt unter der Haube.Sie sehen es nicht im Code, aber jedes Mal, wenn Sie ein Objekt verwenden, müssen alle drei oben genannten Dinge irgendwie passieren. Wenn Sie alles manuell machen würden, wäre der Code offensichtlich viel länger.
Jetzt kann die Abstraktion effizient durchgeführt werden, ohne zusätzlichen Aufwand zu verursachen: Methoden-Inlining und andere Techniken können sowohl von Compilern als auch von Programmierern verwendet werden, um Abstraktionskosten zu entfernen. Dies ist jedoch nicht der Fall.
Was passiert wirklich in C ++?
Hier ist es, aufgeschlüsselt:
std::ios_base
Klasse wird initialisiert. Dies ist die Basisklasse für alles, was mit E / A zu tun hat.std::cout
Objekt wird initialisiert.std::__ostream_insert
(wie Sie bereits anhand des Namens herausgefunden haben) eine Methodestd::cout
(im Grunde der<<
Operator), mit der dem Stream eine Zeichenfolge hinzugefügt wird.cout::endl
wird auch an übergebenstd::__ostream_insert
.__std_dso_handle
wird an übergeben__cxa_atexit
, eine globale Funktion, die für das "Reinigen" vor dem Beenden des Programms verantwortlich ist.__std_dso_handle
selbst wird von dieser Funktion aufgerufen, um die Zuordnung und Zerstörung verbleibender globaler Objekte aufzuheben.Also mit C == nichts bezahlen?
Im C-Code werden nur sehr wenige Schritte ausgeführt:
puts
über dasedi
Register weitergeleitet.puts
wird gerufen.Keine Objekte irgendwo, daher muss nichts initialisiert / zerstört werden.
Dies bedeutet jedoch nicht, dass Sie für nichts in C "bezahlen" . Sie zahlen immer noch für die Abstraktion, und auch die Initialisierung der C-Standardbibliothek und die dynamische Auflösung der
printf
Funktion (oder tatsächlichputs
, die vom Compiler optimiert wird, da Sie keine Formatzeichenfolge benötigen) finden immer noch unter der Haube statt.Wenn Sie dieses Programm in reiner Assemblierung schreiben würden, würde es ungefähr so aussehen:
Dies führt im Grunde nur zum Aufrufen des
write
Systemaufrufs, gefolgt vomexit
Systemaufruf. Nun dies wäre das absolute Minimum , das Gleiche zu erreichen.Zusammenfassen
C ist weitaus einfacher und stellt nur das Nötigste dar, das benötigt wird, und überlässt dem Benutzer die volle Kontrolle, der in der Lage ist, im Grunde alles zu optimieren und anzupassen, was er will. Sie weisen den Prozessor an, eine Zeichenfolge in ein Register zu laden und dann eine Bibliotheksfunktion aufzurufen, um diese Zeichenfolge zu verwenden. C ++ hingegen ist viel komplexer und abstrakter . Dies hat einen enormen Vorteil beim Schreiben von kompliziertem Code und ermöglicht ein einfacheres Schreiben und einen benutzerfreundlicheren Code, ist jedoch offensichtlich mit Kosten verbunden. In solchen Fällen wird die Leistung in C ++ im Vergleich zu C immer einen Nachteil haben, da C ++ mehr bietet, als für die Ausführung solcher grundlegenden Aufgaben erforderlich ist, und somit mehr Overhead verursacht .
Beantwortung Ihrer Hauptfrage :
In diesem speziellen Fall ja . Sie nutzen nichts, was C ++ mehr zu bieten hat als C, aber das liegt nur daran, dass dieser einfache Code nichts enthält, bei dem C ++ Ihnen helfen könnte: Es ist so einfach, dass Sie C ++ wirklich überhaupt nicht benötigen.
Oh, und noch eine Sache!
Die Vorteile von C ++ sind auf den ersten Blick vielleicht nicht offensichtlich, da Sie ein sehr einfaches und kleines Programm geschrieben haben. Schauen Sie sich jedoch ein etwas komplexeres Beispiel an und sehen Sie den Unterschied (beide Programme machen genau das Gleiche):
C :
C ++ :
Hoffentlich können Sie klar sehen, was ich hier meine. Beachten Sie auch, wie Sie in C den Speicher auf einer niedrigeren Ebene verwalten müssen
malloc
undfree
wie Sie bei der Indizierung und Größe vorsichtiger sein müssen und wie Sie bei der Eingabe und beim Drucken sehr spezifisch sein müssen.quelle
Zunächst gibt es einige Missverständnisse. Erstens führt das C ++ - Programm nicht zu 22 Anweisungen, sondern eher zu 22.000 (ich habe diese Nummer aus meinem Hut gezogen, aber sie befindet sich ungefähr im Baseballstadion). Auch der C-Code nicht zu 9 Anweisungen. Das sind nur die, die du siehst.
Der C-Code ruft nach vielen Vorgängen, die Sie nicht sehen, eine Funktion aus der CRT auf (die normalerweise, aber nicht unbedingt als gemeinsam genutzte Bibliothek vorhanden ist) und überprüft dann nicht den Rückgabewert oder das Handle Fehler und rettet. Je nach Compiler und Optimierungseinstellungen es wirklich nicht einmal nennen ,
printf
aberputs
, oder etwas noch primitiv.Sie hätten auch mehr oder weniger dasselbe Programm (mit Ausnahme einiger unsichtbarer Init-Funktionen) in C ++ schreiben können, wenn Sie nur dieselbe Funktion auf dieselbe Weise aufgerufen hätten. Oder, wenn Sie sehr korrekt sein möchten, wird dieselbe Funktion vorangestellt
std::
.Der entsprechende C ++ - Code ist in Wirklichkeit überhaupt nicht dasselbe. Während das Ganze als
<iostream>
bekanntes fettes hässliches Schwein bekannt ist, das kleinen Programmen einen immensen Overhead hinzufügt (in einem "echten" Programm merkt man nicht so viel), ist eine etwas gerechtere Interpretation, dass es schrecklich ist viele Sachen, die du nicht siehst und die einfach funktionieren . Einschließlich, aber nicht beschränkt auf die magische Formatierung so ziemlich aller zufälligen Dinge, einschließlich verschiedener Zahlenformate und Gebietsschemas und so weiter, sowie Pufferung und ordnungsgemäße Fehlerbehandlung. Fehlerbehandlung? Nun ja, raten Sie mal, die Ausgabe eines Strings kann tatsächlich fehlschlagen, und im Gegensatz zum C-Programm würde das C ++ - Programm dies nicht stillschweigend ignorieren. Überlegen wasstd::ostream
tut unter der Haube, und ohne dass es jemand merkt, ist es eigentlich ziemlich leicht. Nicht so, als würde ich es benutzen, weil ich die Stream-Syntax aus Leidenschaft hasse. Trotzdem ist es ziemlich großartig, wenn man bedenkt, was es tut.Aber sicher ist C ++ insgesamt nicht so effizient wie C sein kann. Es kann nicht so effizient sein , da es nicht dasselbe ist , und es ist nicht zu tun , die gleiche Sache. Wenn nichts anderes, generiert C ++ Ausnahmen (und Code zum Generieren, Behandeln oder Fehlschlagen) und gibt einige Garantien, die C nicht gibt. Ein C ++ - Programm muss also unbedingt etwas größer sein. Im Großen und Ganzen spielt dies jedoch keine Rolle. Im Gegenteil, für echte Programme habe ich nicht selten festgestellt, dass C ++ eine bessere Leistung erbringt, da es aus dem einen oder anderen Grund günstigere Optimierungen zu bieten scheint. Fragen Sie mich nicht, warum ich es nicht wissen würde.
Wenn Sie anstelle von Feuer und Vergessen der Hoffnung auf das Beste C-Code schreiben möchten, der korrekt ist (dh Sie suchen tatsächlich nach Fehlern und das Programm verhält sich bei Vorhandensein von Fehlern korrekt), ist der Unterschied gering. falls vorhanden.
quelle
std::cout
wirft es auch Ausnahmen?std::cout
ist einstd::basic_ostream
und das kann man werfen, und es kann ansonsten auftretende Ausnahmen erneut auslösen, wenn es dafür konfiguriert ist , oder es kann Ausnahmen schlucken. Die Sache ist, Dinge können fehlschlagen, und C ++ sowie die C ++ - Standardbibliothek sind (meistens) so aufgebaut, dass Fehler nicht leicht unbemerkt bleiben. Dies ist ein Ärger und ein Segen (aber mehr Segen als Ärger). C hingegen zeigt Ihnen nur den Mittelfinger. Sie überprüfen keinen Rückkehrcode, Sie wissen nie, was passiert ist.Sie bezahlen für einen Fehler. In den 80er Jahren, als Compiler nicht gut genug waren, um Formatzeichenfolgen zu überprüfen, wurde das Überladen von Operatoren als ein guter Weg angesehen, um einen gewissen Anschein von Typensicherheit während io zu erzwingen. Alle Bannerfunktionen sind jedoch von Anfang an entweder schlecht implementiert oder konzeptionell bankrott:
<iomanip>
Der abstoßendste Teil der C ++ - Stream-API ist die Existenz dieser Formatierungsheaderbibliothek. Es ist nicht nur zustandsbehaftet und hässlich und fehleranfällig, sondern koppelt auch die Formatierung an den Stream.
Angenommen, Sie möchten eine Zeile mit einem 8-stelligen, mit Nullen gefüllten hexadezimalen Int ohne Vorzeichen ausdrucken, gefolgt von einem Leerzeichen, gefolgt von einem Doppel mit 3 Dezimalstellen. Mit
<cstdio>
können Sie eine prägnante Formatzeichenfolge lesen. Mit<ostream>
müssen Sie den alten Status speichern, die Ausrichtung auf rechts setzen, das Füllzeichen festlegen, die Füllbreite festlegen, die Basis auf hex setzen, die Ganzzahl ausgeben, den gespeicherten Status wiederherstellen (andernfalls verschmutzt Ihre Ganzzahlformatierung Ihre Gleitkommaformatierung) und das Leerzeichen ausgeben Setzen Sie die Notation auf fest, stellen Sie die Genauigkeit ein, geben Sie die doppelte und die neue Zeile aus und stellen Sie dann die alte Formatierung wieder her.Überlastung des Bedieners
<iostream>
ist das Aushängeschild, wie man das Überladen von Operatoren nicht verwendet:Performance
std::cout
ist um ein Vielfaches langsamerprintf()
. Die grassierende Featuritis und der virtuelle Versand fordern ihren Tribut.Gewindesicherheit
Beide
<cstdio>
und<iostream>
sind insofern threadsicher, als jeder Funktionsaufruf atomar ist. Aberprintf()
pro Anruf wird viel mehr erledigt. Wenn Sie das folgende Programm mit der<cstdio>
Option ausführen , wird nur eine Zeile von angezeigtf
. Wenn Sie<iostream>
auf einem Multicore-Computer verwenden, sehen Sie wahrscheinlich etwas anderes.Die Antwort auf dieses Beispiel ist, dass die meisten Leute Disziplin üben, um niemals aus mehreren Threads in einen einzelnen Dateideskriptor zu schreiben. Nun, in diesem Fall müssen Sie beachten, dass dies
<iostream>
hilfreich ist, um jeden<<
einzelnen zu sperren>>
. Während in<cstdio>
, werden Sie nicht so oft sperren, und Sie haben sogar die Möglichkeit, nicht zu sperren.<iostream>
verbraucht mehr Sperren, um ein weniger konsistentes Ergebnis zu erzielen.quelle
std::cout
Ist um ein Vielfaches langsamerprintf()
" - Diese Behauptung wird im ganzen Netz wiederholt, ist aber seit Ewigkeiten nicht mehr wahr. Moderne IOstream-Implementierungen arbeiten auf Augenhöhe mitprintf
. Letzterer führt auch intern einen virtuellen Versand durch, um gepufferte Streams und lokalisierte E / A zu verarbeiten (vom Betriebssystem ausgeführt, aber dennoch ausgeführt).printf
undcout
. Übrigens gibt es auf dieser Website unzählige solcher Benchmarks.Zusätzlich zu dem, was alle anderen Antworten gesagt haben,
gibt es auch die Tatsache , dass
std::endl
ist nicht das gleiche wie'\n'
.Dies ist ein leider weit verbreitetes Missverständnis.
std::endl
bedeutet nicht "neue Zeile",sondern "neue Zeile drucken und dann den Stream leeren ". Spülen ist nicht billig!
Wenn Sie die Unterschiede zwischen
printf
undstd::cout
für einen Moment vollständig ignorieren , um funktional mit Ihrem C-Beispiel identisch zu sein, sollte Ihr C ++ - Beispiel folgendermaßen aussehen:Und hier ist ein Beispiel dafür, wie Ihre Beispiele aussehen sollten, wenn Sie das Spülen einschließen.
C.
C ++
Wenn Sie Code vergleichen, sollten Sie immer darauf achten, dass Sie Gleiches für Gleiches vergleichen und die Auswirkungen der Funktionsweise Ihres Codes verstehen. Manchmal sind selbst die einfachsten Beispiele komplizierter, als manche Leute glauben.
quelle
std::endl
ist das funktionelle Äquivalent eine neue Zeile in eine Zeile-gepufferte Stdio Strom zu schreiben.stdout
Insbesondere muss es entweder zeilengepuffert oder ungepuffert sein, wenn es an ein interaktives Gerät angeschlossen ist. Ich glaube, Linux besteht auf der zeilengepufferten Option.std::endl
Zeilenpufferung zu erzielen, müssen Sie genau Zeilenumbrüche ausgeben.setvbuf(3)
? Oder wollen Sie damit sagen, dass der Standardwert zeilengepuffert ist? Zu Ihrer Information: Normalerweise sind alle Dateien blockgepuffert. Wenn sich ein Stream auf ein Terminal bezieht (wie es normalerweise bei stdout der Fall ist), wird er zeilengepuffert. Der Standardfehlerstrom stderr ist standardmäßig immer ungepuffert.printf
Wird nicht automatisch gespült, wenn ein Zeilenumbruch auftritt?Obwohl die vorhandenen technischen Antworten korrekt sind, denke ich, dass die Frage letztendlich auf diesem Missverständnis beruht:
Dies ist nur ein Marketinggespräch der C ++ - Community. (Um fair zu sein, gibt es in jeder Sprachgemeinschaft Marketinggespräche.) Es bedeutet nichts Konkretes, auf das Sie sich ernsthaft verlassen können.
"Sie zahlen für das, was Sie verwenden" soll bedeuten, dass eine C ++ - Funktion nur dann Overhead hat, wenn Sie diese Funktion verwenden. Aber die Definition von „einem Merkmal“ ist nicht unendlich körnig. Oft werden Sie Features aktivieren, die mehrere Aspekte haben, und obwohl Sie nur eine Teilmenge dieser Aspekte benötigen, ist es für die Implementierung oft nicht praktikabel oder möglich, die Funktion teilweise einzubringen.
Im Allgemeinen streben viele (wenn auch wohl nicht alle) Sprachen nach Effizienz und unterschiedlichem Erfolg. C ++ ist irgendwo auf der Skala, aber es gibt nichts Besonderes oder Magisches an seinem Design, das es ihm ermöglichen würde, dieses Ziel perfekt zu erreichen.
quelle
<cstdio>
und nicht einschließen<iostream>
, genau wie Sie damit kompilieren können-fno-exceptions -fno-rtti -fno-unwind-tables -fno-asynchronous-unwind-tables
.Die Eingabe- / Ausgabefunktionen in C ++ sind elegant geschrieben und so gestaltet, dass sie einfach zu verwenden sind. In vielerlei Hinsicht sind sie ein Schaufenster für die objektorientierten Funktionen in C ++.
Sie geben zwar im Gegenzug ein wenig Leistung auf, aber das ist vernachlässigbar im Vergleich zu der Zeit, die Ihr Betriebssystem benötigt, um die Funktionen auf einer niedrigeren Ebene auszuführen.
Sie können jederzeit auf die Funktionen im C-Stil zurückgreifen, da diese Teil des C ++ - Standards sind, oder die Portabilität ganz aufgeben und direkte Aufrufe an Ihr Betriebssystem verwenden.
quelle
std::basic_*stream
abwärts), kennt die eingehenden Probleme. Sie waren so konzipiert, dass sie allgemein gehalten und durch Vererbung erweitert wurden. Aber aufgrund ihrer Komplexität (es gibt buchstäblich Bücher über iostreams) hat das letztendlich niemand getan, so dass neue Bibliotheken nur dafür geboren wurden (z. B. Boost, ICU usw.). Ich bezweifle, dass wir jemals aufhören werden, für diesen Fehler zu bezahlen.Wie Sie in anderen Antworten gesehen haben, zahlen Sie, wenn Sie allgemeine Bibliotheken verknüpfen und komplexe Konstruktoren aufrufen. Hier gibt es keine besondere Frage, eher eine Beschwerde. Ich werde auf einige reale Aspekte hinweisen:
Barne hatte ein zentrales Designprinzip, bei dem Effizienz niemals ein Grund dafür war, in C statt in C ++ zu bleiben. Das heißt, man muss vorsichtig sein, um diese Wirkungsgrade zu erzielen, und es gibt gelegentliche Wirkungsgrade, die immer funktionierten, aber nicht „technisch“ innerhalb der C-Spezifikation lagen. Beispielsweise wurde das Layout von Bitfeldern nicht wirklich angegeben.
Versuchen Sie, durch den Strom zu schauen. Oh mein Gott, es ist aufgebläht! Es würde mich nicht wundern, dort einen Flugsimulator zu finden. Sogar stdlibs printf () läuft normalerweise ungefähr 50K. Dies sind keine faulen Programmierer: Die Hälfte der Druckgröße hatte mit indirekten Präzisionsargumenten zu tun, die die meisten Leute nie verwenden. Fast jede wirklich eingeschränkte Prozessorbibliothek erstellt anstelle von printf einen eigenen Ausgabecode.
Die Vergrößerung bietet normalerweise eine geschlossenere und flexiblere Erfahrung. Analog dazu verkauft ein Verkaufsautomat eine Tasse kaffeeähnliche Substanz für ein paar Münzen, und die gesamte Transaktion dauert weniger als eine Minute. Um in ein gutes Restaurant zu kommen, muss man einen Tisch decken, sitzen, bestellen, warten, eine schöne Tasse bekommen, eine Rechnung bekommen, in den von Ihnen gewählten Formen bezahlen, ein Trinkgeld hinzufügen und sich auf dem Weg nach draußen einen guten Tag wünschen. Es ist eine andere Erfahrung und bequemer, wenn Sie mit Freunden zu einer komplexen Mahlzeit kommen.
Die Leute schreiben immer noch ANSI C, obwohl selten K & R C. Ich habe die Erfahrung gemacht, dass wir es immer mit einem C ++ - Compiler kompilieren, indem wir ein paar Konfigurationsänderungen vornehmen, um das Ziehen einzuschränken. Es gibt gute Argumente für andere Sprachen: Go entfernt den polymorphen Overhead und den verrückten Präprozessor ;; Es gab einige gute Argumente für eine intelligentere Feldverpackung und ein intelligenteres Speicherlayout. IMHO Ich denke, jedes Sprachdesign sollte mit einer Auflistung von Zielen beginnen, ähnlich wie das Zen von Python .
Es war eine lustige Diskussion. Sie fragen sich, warum Sie keine magisch kleinen, einfachen, eleganten, vollständigen und flexiblen Bibliotheken haben können?
Es gibt keine Antwort. Es wird keine Antwort geben. Das ist die Antwort.
quelle