Ist es eine schlechte Praxis, einen C ++ - Compiler nur zum Überladen von Funktionen zu verwenden?
IMHO-Standpunkt, ja, und ich muss schizophren werden, um diesen zu beantworten, da ich beide Sprachen liebe, aber es hat nichts mit Effizienz zu tun, sondern eher mit Sicherheit und idiomatischem Sprachgebrauch.
C Seite
Vom C-Standpunkt aus finde ich es so verschwenderisch, dass Ihr Code C ++ erfordert, nur um das Überladen von Funktionen zu verwenden. Sofern Sie es nicht für statischen Polymorphismus mit C ++ - Vorlagen verwenden, ist es ein so trivialer syntaktischer Zucker, der im Austausch für den Wechsel zu einer völlig anderen Sprache gewonnen wird. Wenn Sie Ihre Funktionen jemals in eine Dylib exportieren möchten (kann ein praktisches Problem sein oder auch nicht), können Sie dies nicht mehr sehr praktisch für den weit verbreiteten Gebrauch mit allen mit Namen versehenen Symbolen tun.
C ++ - Seite
Aus C ++ - Sicht sollten Sie C ++ nicht wie C mit Funktionsüberladung verwenden. Dies ist kein stilistischer Dogmatismus, sondern ein Dogmatismus, der sich auf die praktische Anwendung des alltäglichen C ++ bezieht.
Ihre normale Art von C-Code ist nur dann einigermaßen vernünftig und "sicher" zu schreiben, wenn Sie gegen das C-Typ-System arbeiten, das das Kopieren von C-Codes verbietet structs
. Sobald Sie in C ++ 's viel umfangreicherem Typensystem arbeiten, sollten Sie sich ständig auf Funktionen stützen , die von enormem Wert sind memset
und memcpy
nicht zu Funktionen werden. Stattdessen sind es Funktionen, die Sie im Allgemeinen vermeiden möchten, wie die Pest, da Sie sie bei C ++ - Typen nicht wie unformatierte Bits und Bytes behandeln sollten, die kopiert, gemischt und freigegeben werden sollen. Selbst wenn Ihr Code memset
derzeit nur Dinge wie bei Primitiven und POD-UDTs verwendet, fügt jeder Benutzer einem von Ihnen verwendeten UDT einen ctor hinzu (einschließlich des Hinzufügens eines Members, für das ein solcher erforderlich ist, z. B.std::unique_ptr
member) gegen solche Funktionen oder eine virtuelle Funktion oder irgendetwas in dieser Art macht es Ihre gesamte normale C-artige Codierung anfällig für undefiniertes Verhalten. Nimm es von Herb Sutter selbst:
memcpy
und das memcmp
Typensystem verletzen. Das memcpy
Kopieren von Objekten ist wie das Geldverdienen mit einem Fotokopierer. Das memcmp
Vergleichen von Objekten ist wie das Vergleichen von Leoparden durch Zählen ihrer Flecken. Die Tools und Methoden scheinen die Aufgabe zu erfüllen, sind jedoch zu grob, um sie akzeptabel auszuführen. Bei C ++ - Objekten geht es ausschließlich um das Verbergen von Informationen (wohl das rentabelste Prinzip in der Softwareentwicklung; siehe Punkt 11): Objekte verbergen Daten (siehe Punkt 41) und entwickeln präzise Abstraktionen zum Kopieren dieser Daten durch Konstruktoren und Zuweisungsoperatoren (siehe Punkte 52 bis 55). . Bulldozing über all das mit memcpy
ist eine schwerwiegende Verletzung des Versteckens von Informationen und führt oft zu Speicher- und Ressourcenlecks (bestenfalls), Abstürzen (schlimmer) oder undefiniertem Verhalten (schlimmstenfalls) - C ++ Coding Standards.
So viele C-Entwickler würden dem zu Recht widersprechen, da die Philosophie nur gilt, wenn Sie Code in C ++ schreiben. Sie höchstwahrscheinlich werden das Schreiben sehr problematischen Code , wenn Sie Funktionen wie verwenden memcpy
alle Zeiten in Code, der als C ++ erstellt , aber es ist völlig in Ordnung , wenn man es in C . Die beiden Sprachen sind in dieser Hinsicht aufgrund der Unterschiede im Typensystem sehr unterschiedlich. Es ist sehr verlockend, die Teilmenge der Funktionen zu betrachten, die diese beiden gemeinsam haben, und zu glauben, dass eine wie die andere verwendet werden kann, insbesondere auf der C ++ - Seite, aber C + -Code (oder C-- -Code) ist im Allgemeinen weitaus problematischer als sowohl C als auch C ++ Code.
Ebenso sollten Sie beispielsweise nicht malloc
in einem C- ähnlichen Kontext (der keine EH impliziert) arbeiten, wenn C ++ - Funktionen direkt aufgerufen werden können, die ausgelöst werden können, da Sie dann einen impliziten Exit-Punkt in Ihrer Funktion als Ergebnis von haben Ausnahme, die Sie nicht effektiv abfangen können, free
wenn Sie Code im C-Stil schreiben, bevor Sie in den Speicher zugreifen können . Also , wenn Sie eine Datei, die als C ++ baut mit einer .cpp
Erweiterung oder was auch immer und es tut all diesen Arten von Dingen wie malloc
, memcpy
, memset
,qsort
Wenn nicht bereits, wird nach Problemen gefragt, es sei denn, es handelt sich um das Implementierungsdetail einer Klasse, die nur mit primitiven Typen arbeitet. Zu diesem Zeitpunkt muss die Ausnahmebehandlung noch ausgeführt werden, um ausnahmesicher zu sein. Wenn Sie C ++ Code schreiben wollen Sie stattdessen im Allgemeinen auf RAII verlassen verwenden , Dinge wie vector
, unique_ptr
, shared_ptr
usw., und vermeiden Sie alle normalen C-Stil - Codierung , wenn möglich.
Der Grund, warum Sie mit Rasierklingen in C- und Röntgendatentypen spielen und mit ihren Bits und Bytes spielen können, ohne Kollateralschäden in einem Team zu verursachen (obwohl Sie sich trotzdem so oder so wirklich verletzen können), liegt nicht an dem, was C Typen können , aber aufgrund dessen, was sie niemals können. In dem Moment, in dem Sie das Typensystem von C erweitern, um C ++ - Funktionen wie ctors, dtors und vtables zusammen mit der Ausnahmebehandlung einzubeziehen, wird der gesamte idiomatische C-Code weitaus gefährlicher gerendert, als er derzeit ist, und Sie werden eine neue Art von sehen Entwicklung von Philosophie und Denkweise, die eine völlig andere Art der Codierung fördern, wie Sie in C ++ sehen, das jetzt sogar die Verwendung eines rohen Zeigerfehlverhaltens für eine Klasse in Betracht zieht, die das Gedächtnis verwaltet, im Gegensatz zu beispielsweise einer RAII-konformen Ressource wie unique_ptr
. Diese Einstellung ist nicht aus einem absoluten Sicherheitsgefühl entstanden. Es hat sich aus dem entwickelt, was C ++ speziell für Funktionen wie die Ausnahmebehandlung benötigt, wenn man bedenkt, was es nur durch sein Typensystem zulässt .
Ausnahmesicherheit
Sobald Sie sich in C ++ befinden, erwarten die Benutzer, dass Ihr Code ausnahmesicher ist. In Zukunft wird Ihr Code möglicherweise gewartet, vorausgesetzt, er ist bereits in C ++ geschrieben und kompiliert und wird einfach std::vector, dynamic_cast, unique_ptr, shared_ptr
in Code verwendet, der entweder direkt oder indirekt von Ihrem Code aufgerufen wird Code. An diesem Punkt müssen wir uns der Chance stellen, dass die Dinge sich auswirken, und dann, wenn Sie perfekten und schönen C-Code wie diesen nehmen:
int some_func(int n, ...)
{
int* x = calloc(n, sizeof(int));
if (x)
{
f(n, x); // some function which, now being a C++ function, may
// throw today or in the future.
...
free(x);
return success;
}
return fail;
}
... es ist jetzt kaputt. Es muss umgeschrieben werden, um ausnahmesicher zu sein:
int some_func(int n, ...)
{
int* x = calloc(n, sizeof(int));
if (x)
{
try
{
f(n, x); // some function which, now being a C++ function, may
// throw today or in the future (maybe someone used
// std::vector inside of it).
}
catch (...)
{
free(x);
throw;
}
...
free(x);
return success;
}
return fail;
}
Brutto! Aus diesem Grund würden die meisten C ++ - Entwickler dies stattdessen fordern:
void some_func(int n, ...)
{
vector<int> x(n);
f(x); // some function which, now being a C++ function, may throw today
// or in the future.
}
Die oben ist RAH-konforme Ausnahme-sichere Code der Art , dass C ++ Entwickler im Allgemeinen , da die Funktion Leck nicht nein , die Codezeile löst einen impliziten Ausgang als Folge eines billigen würden throw
.
Wähle eine Sprache
Sie sollten entweder das Typensystem und die Philosophie von C ++ mit RAII, Ausnahmesicherheit, Vorlagen, OOP usw. einbeziehen oder C einbeziehen, das sich hauptsächlich um unformatierte Bits und Bytes dreht. Sie sollten keine unheilige Ehe zwischen diesen beiden Sprachen eingehen, sondern sie in verschiedene Sprachen aufteilen, um sie ganz anders zu behandeln, anstatt sie zu verwischen.
Diese Sprachen wollen dich heiraten. Im Allgemeinen musst du dir eine aussuchen, anstatt dich mit beiden zu verabreden und herumzublödeln. Oder du kannst ein Polygamist wie ich sein und beide heiraten, aber du musst dein Denken völlig ändern, wenn du Zeit miteinander verbringst, und sie gut voneinander getrennt halten, damit sie sich nicht bekämpfen.
Binäre Größe
Aus Neugier habe ich gerade versucht, meine kostenlose Listenimplementierung und meinen kostenlosen Benchmark auf C ++ zu portieren, da ich wirklich neugierig geworden bin:
[...] weiß nicht, wie es für C aussehen würde, weil ich den C-Compiler nicht verwendet habe.
... und wollte wissen, ob sich die Binärgröße überhaupt aufblähen würde, wenn man nur C ++ baut. Es erforderte, dass ich explizite Casts über den ganzen Ort verteilte, was nur eine Minute dauerte (ein Grund, warum ich es mag, Low-Level-Dinge wie Allokatoren und Datenstrukturen besser in C zu schreiben).
Dies war nur ein Vergleich eines MSVC-64-Bit-Release-Builds für eine einfache Konsolenanwendung mit Code, der keine C ++ - Funktionen verwendete, nicht einmal das Überladen von Operatoren - nur der Unterschied zwischen dem Erstellen mit C und der Verwendung von beispielsweise <cstdlib>
anstelle von <stdlib.h>
und solche Dinge, aber ich war überrascht, dass es keinen Unterschied zur Binärgröße machte!
Die Binärdatei bestand aus 9,728
Bytes, als sie in C erstellt wurde, und ebenso aus 9,278
Bytes, als sie als C ++ - Code kompiliert wurde. Das habe ich eigentlich nicht erwartet. Ich dachte, Dinge wie EH würden dort zumindest ein wenig hinzufügen (dachten, es würde sich zumindest um einhundert Bytes unterscheiden), obwohl es wahrscheinlich möglich war, herauszufinden, dass es nicht notwendig war, EH-bezogene Anweisungen hinzuzufügen, da ich nur bin mit der C-Standardbibliothek und nichts löst. Ich dachte etwaswürde ein wenig zur Binärgröße hinzufügen, wie z. B. RTTI. Jedenfalls war es irgendwie cool, das zu sehen. Natürlich denke ich nicht, dass Sie dieses eine Ergebnis verallgemeinern sollten, aber es hat mich zumindest ein bisschen beeindruckt. Es hat auch keinen Einfluss auf die Benchmarks, und natürlich, da ich mir vorstelle, dass die identische resultierende Binärgröße auch identische resultierende Maschinenbefehle bedeutete.
Wer kümmert sich um die Binärgröße bei den oben genannten Sicherheits- und Engineering-Problemen? Suchen Sie sich also erneut eine Sprache aus und nehmen Sie deren Philosophie an, anstatt zu versuchen, sie zu verfälschen. das empfehle ich.
//
Kommentare zu erhalten. Wenn es funktioniert, warum nicht?