Hintergrund / Szenario
Ich habe angefangen, eine CLI-Anwendung nur in C zu schreiben (mein erstes richtiges C- oder C ++ - Programm, das nicht "Hello World" oder eine Variation davon war). Ungefähr in der Mitte arbeitete ich mit "Strings" von Benutzereingaben (char-Arrays) und entdeckte das C ++ - String-Streamer-Objekt. Ich habe gesehen, dass ich mit diesen Code speichern kann, also habe ich sie über die Anwendung verwendet. Dies bedeutet, dass ich die Dateierweiterung in .cpp geändert habe und jetzt die App mit g++
anstelle von kompiliere gcc
. Auf dieser Grundlage würde ich sagen, dass die Anwendung jetzt technisch gesehen eine C ++ - Anwendung ist (obwohl 90% + des Codes in dem geschrieben sind, was ich C nennen würde, da es aufgrund meiner begrenzten Erfahrung mit beiden Sprachen viele Überschneidungen gibt die Zwei). Es ist eine einzelne CPP-Datei mit einer Länge von etwa 900 Zeilen.
Wichtige Faktoren
Ich möchte, dass das Programm kostenlos (wie in Geld) und frei verteilbar und für alle nutzbar ist. Ich mache mir Sorgen, dass sich jemand den Code ansieht und etwas überlegt, um Folgendes zu bewirken:
Oh, sieh dir die Codierung an, es ist schrecklich, dieses Programm kann mir nicht helfen
Wenn es möglich sein könnte! Eine andere Sache ist, dass der Code effizient ist (es ist ein Programm zum Testen der Ethernet-Konnektivität). Es sollten keine Teile des Codes vorhanden sein, die so ineffizient sind, dass sie die Leistung der Anwendung oder ihre Ausgabe erheblich beeinträchtigen können. Ich denke jedoch, dass dies eine Frage für den Stapelüberlauf ist, wenn Sie um Hilfe bei bestimmten Funktionen, Methoden, Objektaufrufen usw. bitten.
Meine Frage
Ich habe (meiner Meinung nach) C und C ++ gemischt, wo ich es vielleicht nicht sollte. Sollte ich versuchen, alles in C ++ neu zu schreiben (damit meine ich, mehr C ++ - Objekte und -Methoden zu implementieren, bei denen ich möglicherweise etwas in einem C-Stil codiert habe, das mit neueren C ++ - Techniken komprimiert werden kann), oder die Verwendung von String-Streamer-Objekten und entfernen alles "zurück" zum C-Code bringen? Gibt es hier einen richtigen Ansatz? Ich bin verloren und brauche eine Anleitung, wie ich diese Anwendung in den Augen der Massen "gut" halten kann, damit sie sie nutzen und davon profitieren können.
Der Code - Update
Hier ist ein Link zum Code. Es sind ca. 40% Kommentare, ich kommentiere fast jede Zeile, bis ich mich fließender fühle. In der Kopie, auf die ich verlinkt habe, habe ich so ziemlich alle Kommentare entfernt. Ich hoffe, das macht es nicht zu schwer zu lesen. Ich hoffe jedoch, dass niemand es vollständig verstehen muss. Wenn ich jedoch schwerwiegende Designfehler gemacht habe, hoffe ich, dass sie leicht zu identifizieren sind. Ich sollte auch erwähnen, dass ich ein paar Ubuntu-Desktops und -Laptops schreibe. Ich beabsichtige nicht, den Code auf andere Betriebssysteme zu portieren.
LICENSE
Datei. Möglicherweise erhalten Sie interessantes Feedback.Antworten:
Beginnen wir am Anfang: Gemischter C- und C ++ - Code ist ziemlich häufig. Sie sind also zunächst in einem großen Club. Wir haben riesige C-Codebasen in freier Wildbahn. Aber aus offensichtlichen Gründen weigern sich viele Programmierer, zumindest neue Inhalte in C zu schreiben, da sie Zugriff auf C ++ im selben Compiler haben. Neue Module werden auf diese Weise geschrieben - zunächst werden nur die vorhandenen Teile in Ruhe gelassen.
Dann werden schließlich einige vorhandene Dateien als C ++ neu kompiliert und einige Bridges können gelöscht werden ... Aber es kann sehr lange dauern.
Sie sind etwas voraus, Ihr vollständiges System ist jetzt C ++, nur das meiste davon ist im "C-Stil" geschrieben. Und Sie sehen, dass Stilmix ein Problem ist, was Sie nicht sollten: C ++ ist eine Multi-Paradigmen-Sprache, die viele Stile unterstützt und es ihnen ermöglicht, für immer nebeneinander zu existieren. Eigentlich ist das die Hauptstärke, dass man nicht zu einem einzigen Stil gezwungen ist. Eine, die hier und da suboptimal wäre, mit etwas Glück nicht überall.
Die Codebasis zu überarbeiten ist eine gute Idee, wenn sie kaputt ist. Oder wenn es der Entwicklung im Wege steht. Aber wenn es funktioniert (im ursprünglichen Sinne des Wortes), befolgen Sie bitte das grundlegendste technische Prinzip: Wenn es nicht kaputt ist, beheben Sie es nicht. Lassen Sie die kalten Teile in Ruhe und geben Sie sich Mühe, wo es darauf ankommt. Auf den Teilen, die schlecht, gefährlich sind - oder in neuen Funktionen, und nur Teile umgestalten, um sie zu einem Bett zu machen.
Wenn Sie nach allgemeinen Fragen suchen, sollten Sie Folgendes aus einer C-Codebasis entfernen:
Wenn Sie damit fertig sind, nähern Sie sich dem Punkt, an dem die Codebasis einigermaßen ausnahmesicher sein kann, sodass Sie den Rückkehrcode-Fußball nur mit Ausnahmen und seltenen Versuchen / Fangen in Funktionen auf hoher Ebene löschen können.
Darüber hinaus schreiben Sie einfach neuen Code in einwandfreiem C ++, und wenn einige Klassen geboren werden, die einen guten Ersatz für vorhandenen Code darstellen, holen Sie sie ab.
Ich habe keine syntaxbezogenen Dinge erwähnt, offensichtlich Refs anstelle von Zeigern in allen neuen Codes verwendet, aber das Ersetzen alter C-Teile nur für diese Änderung ist kein guter Wert. Casts, die Sie adressieren, eliminieren und für den Rest C ++ - Varianten in Wrapper-Funktionen verwenden müssen. Und was sehr wichtig ist, fügen Sie gegebenenfalls const hinzu. Diese verschachteln mit den früheren Kugeln. Konsolidieren Sie Ihre Makros und ersetzen Sie das, was Sie in Aufzählung, Inline-Funktion oder Vorlage umwandeln können.
Ich empfehle, die C ++ - Codierungsstandards von Sutter / Alexandrescu zu lesen, falls dies noch nicht geschehen ist, und sie genau zu befolgen.
quelle
Kurz gesagt: Du wirst nicht zur Hölle fahren, nichts Schlimmes wird passieren, aber du wirst auch keine Schönheitswettbewerbe gewinnen.
Es ist zwar durchaus möglich, C ++ als "besseres C" zu verwenden, aber Sie werfen viele der Vorteile von C ++ weg, aber weil Sie sich nicht auf Vanille C beschränken, erhalten Sie keine der Vorteile von C (Einfachheit, Portabilität) , Transparenz, Interoperabilität). Mit anderen Worten: C ++ opfert einige der Eigenschaften von C, um andere zu gewinnen. Beispielsweise wird die Transparenz von C, bei der Sie immer klar sehen können, wo und wann Speicherzuweisungen stattfinden, gegen die leistungsstärkeren Abstraktionen von C ++ eingetauscht.
Da Ihr Code jetzt in Ordnung zu sein scheint, ist es wahrscheinlich keine gute Idee, ihn nur deswegen neu zu schreiben: Behalten Sie ihn vorerst bei und ändern Sie ihn Stück für Stück in idiomatischeres C ++. wann immer Sie an einem bestimmten Teil arbeiten. Und denken Sie an die so gewonnenen Erkenntnisse für Ihr nächstes Projekt, vor allem an diese: C und C ++ sind nicht dieselbe Sprache, und Sie sollten eine Entscheidung im Voraus treffen, als sich nach der Hälfte Ihres Projekts für einen Wechsel zu C ++ zu entscheiden .
quelle
C ++ ist eine sehr komplexe Sprache in dem Sinne, dass sie viele Funktionen hat. Es ist auch eine Sprache, die mehrere Paradigmen unterstützt, was bedeutet, dass Sie vollständig prozeduralen Code schreiben können, ohne Objekte zu verwenden.
Da C ++ so komplex ist, sehen Sie häufig, dass Benutzer eine begrenzte Teilmenge seiner Funktionen in ihrem Code verwenden. Anfänger verwenden häufig nur die Stream-E / A, die Zeichenfolgenobjekte und new / delete anstelle von malloc / free und möglicherweise Verweise anstelle von Zeigern. Wenn Sie sich mit den objektorientierten Funktionen vertraut machen, können Sie mit dem Schreiben in einem Stil namens "C mit Klassen" beginnen. Wenn Sie mehr über C ++ erfahren, verwenden Sie schließlich Vorlagen, RAII , STL , intelligente Zeiger usw.
Der Punkt, den ich versuche, ist, dass das Erlernen von C ++ ein Prozess ist, der Zeit braucht. Ja, im Moment sieht Ihr Code wahrscheinlich so aus, als ob er von einem C-Programmierer geschrieben wurde, der versucht, C ++ zu schreiben. Und da Sie gerade lernen, ist das vollkommen in Ordnung. Es erschreckt mich jedoch, wenn ich so etwas im Produktionscode sehe, der von erfahrenen Programmierern geschrieben wurde, die es besser wissen sollten.
Denken Sie daran, ein guter Fortran-Programmierer kann guten Fortran-Code in jeder Sprache schreiben. :) :)
quelle
Es hört sich so an, als würden Sie genau den Weg rekapitulieren, den viele alte C-Programmierer eingeschlagen haben, als sie zu C ++ gewechselt sind. Sie verwenden es als "besseres C". Vor zwanzig Jahren machte dies Sinn, aber zu diesem Zeitpunkt gibt es in C ++ so viele leistungsfähigere Konstrukte (wie die Standardvorlagenbibliothek), dass es jetzt selten Sinn macht. Zumindest sollten Sie wahrscheinlich Folgendes tun, um zu vermeiden, dass C ++ - Programmierer beim Betrachten Ihres Codes Aneurysmen bekommen:
printf
Familie.new
anstelle vonmalloc
.std::string
stattchar*
wo immer möglichWenn Ihr Programm kurz ist (und ~ 900 Zeilen kurz erscheinen), halte ich es persönlich nicht für erforderlich oder sogar nützlich, einen robusten Satz von Klassen zu erstellen.
quelle
In der akzeptierten Antwort werden nur die Vorteile der Konvertierung von C in idiomatisches C ++ erwähnt, als ob C ++ - Code in einem absoluten Sinne besser wäre als C-Code. Ich stimme anderen Antworten zu, dass es wahrscheinlich unnötig ist, drastische Änderungen am gemischten Code vorzunehmen, wenn dieser nicht kaputt ist.
Ob das Mischen von C und C ++ nachhaltig ist, hängt davon ab, wie das Mischen durchgeführt wird. Subtile Fehler können beispielsweise auftreten, wenn Ausnahmen im C-Code ausgelöst werden (was nicht ausnahmesicher ist), was zu Speicherlecks oder Datenbeschädigungen führt. Sicherer und üblicher ist es, C-Bibliotheken oder Schnittstellen in Klassen zu verpacken oder sie in einer anderen Art der Isolation in einem C ++ - Projekt zu verwenden.
Bevor Sie sich entscheiden, Ihr gesamtes Projekt in idiomatisches C ++ (oder C) umzuschreiben, sollten Sie sich darüber im Klaren sein, dass viele der in anderen Antworten enthaltenen Änderungen Ihr Programm verlangsamen oder andere unerwünschte Effekte verursachen können. Zum Beispiel:
Abschließend sollte die Konvertierung von gemischtem C- und C ++ - Code in idiomatisches C ++ als Kompromiss angesehen werden, der viel mehr beinhaltet als nur die Implementierungszeit und die Erweiterung Ihrer Toolbox mit scheinbar praktischen Funktionen. Daher ist es sehr schwierig, eine Antwort auf den allgemeinen Fall zu geben, außer "es kommt darauf an".
quelle
Es ist eine schlechte Form, C und C ++ zu mischen. Sie sind verschiedene Sprachen und sollten wirklich als solche behandelt werden. Wählen Sie diejenige aus, mit der Sie am besten vertraut sind, und versuchen Sie, idiomatischen Code in dieser Sprache zu schreiben.
Wenn C ++ Ihnen viel Code spart, bleiben Sie bei C ++ und schreiben Sie die C-Teile neu. Ich bezweifle, dass ein Leistungsunterschied überhaupt spürbar sein wird, wenn Sie sich darüber Sorgen machen.
quelle
Ich gehe die ganze Zeit zwischen C und C ++ hin und her und bin der seltsame Typ, der es bevorzugt, auf diese Weise zu arbeiten. Ich bevorzuge C ++ für übergeordnete Dinge, während ich C als stumpfes Instrument verwende, mit dem ich Datentypen röntgen und sie wie Bits und Bytes behandeln kann, mit
memcpy
denen ich beispielsweise Datenstrukturen und Speicherzuordnungen auf niedriger Ebene implementieren kann . Wenn Sie auf der Ebene der Rohbits und -bytes arbeiten, hilft das wirklich reichhaltige Typensystem von C ++ überhaupt nichts, und ich finde es oft einfacher, solchen Code in C zu schreiben, wo ich sicher davon ausgehen kann, dass ich es kann Behandeln Sie jeden C-Datentyp nur als Bits und Bytes.Die Hauptsache ist, wo diese beiden Sprachen nicht gut zusammenpassen.
1. Die AC-Funktion sollte niemals eine C ++ - Funktion aufrufen, die dies kann
throw
. Angesichts der Anzahl der Stellen, an denen Ihre tägliche Art von C ++ - Code implizit auf eine Ausnahme stoßen kann, bedeutet dies im Allgemeinen, dass Ihre C-Funktionen im Allgemeinen keine C ++ - Funktionen aufrufen sollten. Andernfalls kann Ihr C-Code keine Ressourcen freigeben, die er beim Abwickeln des Stapels zuweist, da er die C ++ - Ausnahme nicht praktisch abfangen kann. Wenn Ihr C-Code beispielsweise eine C ++ - Funktion als Rückruf aufrufen muss, sollte die C ++ - Seite sicherstellen, dass alle aufgetretenen Ausnahmen abgefangen werden, bevor Sie zum C-Code zurückkehren.2. Wenn Sie C-Code schreiben, der Datentypen nur als Rohbits und -bytes behandelt und über das Typsystem planiert (dies ist eigentlich mein Hauptgrund für die Verwendung von C), möchten Sie diesen Code niemals für C ++ - Daten verwenden Typen, die Kopierkonstruktoren und -destruktoren sowie virtuelle Zeiger und solche Dinge haben könnten, die respektiert werden müssen. Im Allgemeinen sollte es sich also um C-Code mit C-Code dieser Art handeln, nicht um C ++ - Code mit C-Code dieser Art.
Wenn Sie möchten, dass Ihr C ++ - Code solchen C-Code verwendet, möchten Sie ihn normalerweise als Implementierungsdetail einer Klasse verwenden, die sicherstellt, dass die Daten, die in der generischen C-Datenstruktur gespeichert werden, ein Datentyp sind, dessen Erstellung trivial ist und zerstören. Glücklicherweise gibt es Art Züge wie diese , die Sie , dass der Check - in einen statischen assert verwenden können, um sicherzustellen , dass die Typen , die Sie in die generische C - Datenstruktur zu speichern haben trivial Destruktoren und Konstrukteure und wird auch so bleiben.
Meiner Meinung nach brauchen Sie sich nicht darum zu kümmern, wenn Sie die oben genannten Regeln einhalten und sicherstellen, dass Ihr Code gegen die von Ihnen geschriebenen Tests gut getestet ist. Der Teil, der möglicherweise verbessert werden sollte, ist der Code, den Sie bisher in C ++ geschrieben haben. Dies bedeutet jedoch nicht, dass Sie den Code, der ausschließlich C-basiert ist, auf C ++ portieren müssen.
Mit dem Code, den Sie in C ++ geschrieben und als C ++ - Code kompiliert haben, müssen Sie vorsichtig sein. Die Verwendung von C-ähnlicher Codierung in C ++ ist problematisch. Wenn Sie beispielsweise Ressourcen manuell zuweisen und freigeben, besteht die Möglichkeit, dass Ihr Code nicht ausnahmesicher ist, da in Ihrem Code möglicherweise eine Ausnahme auftritt. An diesem Punkt verlassen Sie die Funktion implizit, bevor Sie
free
die Ressource jemals verwenden können. In C ++ sind RAII und Dinge wie intelligente Zeiger keine einfache Annehmlichkeit, da sie möglicherweise auftreten, wenn Sie nur normale Ausführungspfade betrachten, ohne außergewöhnliche Pfade zu berücksichtigen. Sie sind oft eine grundlegende Notwendigkeit, um einfach und effektiv korrekten ausnahmesicheren Code zu schreiben, der nicht überall durchläuft, wenn eine Ausnahme auftritt.quelle