Autor Thema: C oder C++  (Gelesen 15537 mal)

nnosdev

  • Beiträge: 61
    • Profil anzeigen
Gespeichert
« am: 15. February 2012, 21:55 »
Lasst mal was hören Leute ^^

Ich persönlich steh ja absolut auf die Möglichkeit objektorientiert zu programmieren.

Gibts ein paar Tipps bzw Ratschläge die einem helfen können für welche Sprache
man sich konkret entscheidet?

:)

XanClic

  • Beiträge: 261
    • Profil anzeigen
    • github
Gespeichert
« Antwort #1 am: 16. February 2012, 02:09 »
Erstmal ist das ja sowieso eine Glaubensfrage. Wenn du C++ willst, dann solltest du den Kernel auch in C++ programmieren. So einfach ist das.

Um auch noch mehr Senf loszuwerden, könnte ich noch anbieten, dass man in C nichtsdestotrotz auch objektorientiert programmieren kann – die Frage ist dann nur, ob man das will, mit Funktionspointern für Methoden, ohne impliziten this-Pointer und ohne so aparte¹ Sachen wie Konstrukturaufrufen mit new und Destruktoraufrufen mit delete. Ansonsten kenne ich mich vermutlich nicht genug mit C++ aus, um zu wissen, was man sonst noch so haben können wöllte (Überladung sollte man inzwischen in C auch mit einer kräftigen Prise Präprozessormagie hinbekommen). Aber das nur, weil das meiner Meinung nach in jede Diskussion um C vs. C++ gehört.

Und zuletzt gehört in so eine Diskussion, dass C++ längst keine Obermenge mehr von C ist. Seit C99 und jetzt zusätzlich mit C11 gibt es in C Features, die es in C++ nicht gibt (v. a. wären da für mich Arrays variabler Länge zu nennen, sind aber noch einige andere). Wenn du die aber eh nicht nutzt, dann kannst du mit C++ wohl zumindest wenig falsch machen, denke ich.


¹ Das hat mir dict.leo.org als Übersetzung für „fancy“ angeboten, weil mir selbst kein apartes deutsches Wort dafür eingefallen ist. Ich wäre ja für die Einführung des Wortes fehnzig.

LittleFox

  • Beiträge: 306
    • Profil anzeigen
    • LF-Net.org
Gespeichert
« Antwort #2 am: 16. February 2012, 09:37 »
Hi,

meiner Meinung nach geht C++ im Kernel richtig gut. Nur Exceptions und RTTI bekommt man nur mit viel Aufwand hin.
Außerdem brauchst du für einige Features erstmal eine kleine Runtime Library, die auf einige Kernelfeatures aufbaut - bspw. new und delete die auf deiner Speicherverwaltung aufbauen.

Grüße,
LittleFox

kevin

  • Administrator
  • Beiträge: 2 767
    • Profil anzeigen
Gespeichert
« Antwort #3 am: 16. February 2012, 10:51 »
Eigentlich sollte man so einen Thread ja sofort abschließen und den Autor für eine Woche sperren, aber wir hatten schon lange keinen anständigen Flamewar mehr. Wird vielleicht Zeit...

Ich persönlich steh ja absolut auf die Möglichkeit objektorientiert zu programmieren.
Geht mit C wunderbar. :)

Zitat
Gibts ein paar Tipps bzw Ratschläge die einem helfen können für welche Sprache man sich konkret entscheidet?
Man überlegt sich, ob man { oder begin schöner findet und nimmt dann entweder C oder ObjFPC.

Erstmal ist das ja sowieso eine Glaubensfrage. Wenn du C++ willst, dann solltest du den Kernel auch in C++ programmieren. So einfach ist das.
Und hinterher keinem anderen die Schuld dafür geben.

Zitat
Um auch noch mehr Senf loszuwerden, könnte ich noch anbieten, dass man in C nichtsdestotrotz auch objektorientiert programmieren kann – die Frage ist dann nur, ob man das will, mit Funktionspointern für Methoden, ohne impliziten this-Pointer und ohne so aparte¹ Sachen wie Konstrukturaufrufen mit new und Destruktoraufrufen mit delete.
Präprozessor to the rescue?  :-D

Zitat
(Überladung sollte man inzwischen in C auch mit einer kräftigen Prise Präprozessormagie hinbekommen).
Hm, du meinst, mit diesem C11-Union-Parameter-Dingsda-Zeug? Würde ich gern mal in Code sehen, wenn du sowas gebastelt hast.

Zitat
Und zuletzt gehört in so eine Diskussion, dass C++ längst keine Obermenge mehr von C ist. Seit C99 und jetzt zusätzlich mit C11 gibt es in C Features, die es in C++ nicht gibt (v. a. wären da für mich Arrays variabler Länge zu nennen, sind aber noch einige andere). Wenn du die aber eh nicht nutzt, dann kannst du mit C++ wohl zumindest wenig falsch machen, denke ich.
Ui, dass C++ keine Arrays variabler Länge kann, war mir gar nicht bewusst. Die benutze ich ständig.

Das Totschlagargument hier ist ja mittlerweile mit C++11 behoben - es gibt endlich ein long long.

Ansonsten finde ich natürlich Designated Initializers und Compound Literals einen wesentlich Vorteil von C. Außerdem kann C++, wenn ich das richtig sehe, keine anonymen unions und structs.

Zitat
¹ Das hat mir dict.leo.org als Übersetzung für „fancy“ angeboten, weil mir selbst kein apartes deutsches Wort dafür eingefallen ist. Ich wäre ja für die Einführung des Wortes fehnzig.
"schick" hätte ja auch ganz gut gepasst, aber wenn schon, dass bin ich für "fänzig". Wobei mich dieser ganze Eindeutschungsversuch an "happig" für "happy" erinnert. ;)
Thou shalt not follow the NULL pointer, for chaos and madness await thee at its end.

XanClic

  • Beiträge: 261
    • Profil anzeigen
    • github
Gespeichert
« Antwort #4 am: 16. February 2012, 21:49 »
Zitat
(Überladung sollte man inzwischen in C auch mit einer kräftigen Prise Präprozessormagie hinbekommen).
Hm, du meinst, mit diesem C11-Union-Parameter-Dingsda-Zeug? Würde ich gern mal in Code sehen, wenn du sowas gebastelt hast.
Sowas hab nicht ich gebaut. :-D

Irgendwann hab ich mal in den Tiefen des Internets Code gesehen, um Funktionen mit verschiedener Parameteranzahl per CPP zu überladen – mit den _Generic-Sachen sollte das nun auch für verschiedene Parametertypen funktionieren. Aus dem Stegreif hätte ich da also nichts parat.

Zitat
Und zuletzt gehört in so eine Diskussion, dass C++ längst keine Obermenge mehr von C ist. Seit C99 und jetzt zusätzlich mit C11 gibt es in C Features, die es in C++ nicht gibt (v. a. wären da für mich Arrays variabler Länge zu nennen, sind aber noch einige andere). Wenn du die aber eh nicht nutzt, dann kannst du mit C++ wohl zumindest wenig falsch machen, denke ich.
Ui, dass C++ keine Arrays variabler Länge kann, war mir gar nicht bewusst. Die benutze ich ständig.
Tjo, in gnu++ sind die auch drin, aber im ISO-C++ afaik nicht.

nnosdev

  • Beiträge: 61
    • Profil anzeigen
Gespeichert
« Antwort #5 am: 16. February 2012, 21:57 »
Ich persönlich steh ja absolut auf die Möglichkeit objektorientiert zu programmieren.
Geht mit C wunderbar. :)

Mir ist schon klar, dass man mit C objektorientiert programmieren kann. Aber in diesem Falle fällt die
Wahl dann wohl auf C++ da sie ja nicht nur so tut als Dinge einen Zusammenhang hätten wogegen
man in C schon mehr Aufwand betreiben muss um sauber objektorientiert zu programmieren.

Erstmal ist das ja sowieso eine Glaubensfrage. Wenn du C++ willst, dann solltest du den Kernel auch in C++ programmieren. So einfach ist das.
Und hinterher keinem anderen die Schuld dafür geben.

Also hältst du C++ eher für die falsche Wahl? :)

Zitat
Um auch noch mehr Senf loszuwerden, könnte ich noch anbieten, dass man in C nichtsdestotrotz auch objektorientiert programmieren kann – die Frage ist dann nur, ob man das will, mit Funktionspointern für Methoden, ohne impliziten this-Pointer und ohne so aparte¹ Sachen wie Konstrukturaufrufen mit new und Destruktoraufrufen mit delete.
Präprozessor to the rescue?  :-D

Eben das wäre mir zu umständlich. Dann doch lieber C++


Warum benützen wir nicht ganz normal das Wort fancy? Wir sagen doch auch "Manager" und nicht "Leiter" ^

kevin

  • Administrator
  • Beiträge: 2 767
    • Profil anzeigen
Gespeichert
« Antwort #6 am: 17. February 2012, 10:01 »
Mir ist schon klar, dass man mit C objektorientiert programmieren kann. Aber in diesem Falle fällt die
Wahl dann wohl auf C++ da sie ja nicht nur so tut als Dinge einen Zusammenhang hätten wogegen
man in C schon mehr Aufwand betreiben muss um sauber objektorientiert zu programmieren.
Kommt darauf an, was du unter sauber verstehst und was du vorhast. Manche Konstrukte können in C umständlich werden, aber ich behaupte einfach mal, dass man die in der Regel auch nicht braucht.

Wo es allerdings in Sachen "so tun, als gäbe es einen Zusammenhang" (was auch immer du damit meinst) einen Unterschied zwischen C und C++ geben sollte, kann ich im Moment nicht nachvollziehen.

Zitat
Zitat
Um auch noch mehr Senf loszuwerden, könnte ich noch anbieten, dass man in C nichtsdestotrotz auch objektorientiert programmieren kann – die Frage ist dann nur, ob man das will, mit Funktionspointern für Methoden, ohne impliziten this-Pointer und ohne so aparte¹ Sachen wie Konstrukturaufrufen mit new und Destruktoraufrufen mit delete.
Präprozessor to the rescue?  :-D
Eben das wäre mir zu umständlich. Dann doch lieber C++
Das war nicht ernstgemeint. Ich würde den Präprozessor für diese Sachen nicht benutzen. Impliziter this-Pointer, new und delete sind Syntaxdetails. Mein C-Code muss nicht wie C++-Code aussehen. Ich kann den this-Pointer einfach explizit mitgeben und direkt Konstruktor und Destruktor statt new aufrufen.

Zitat
Warum benützen wir nicht ganz normal das Wort fancy? Wir sagen doch auch "Manager" und nicht "Leiter" ^
Tun wir das? Du gehörst wohl auch zu den Leuten, die Laptop statt Klapprechner sagen? ;)
Thou shalt not follow the NULL pointer, for chaos and madness await thee at its end.

nnosdev

  • Beiträge: 61
    • Profil anzeigen
Gespeichert
« Antwort #7 am: 17. February 2012, 12:59 »
Wo es allerdings in Sachen "so tun, als gäbe es einen Zusammenhang" (was auch immer du damit meinst) einen Unterschied zwischen C und C++ geben sollte, kann ich im Moment nicht nachvollziehen.
Damit ist Code wie dieser gemeint ^^
typedef struct
{
    int anzahlRaeder;
    char* hupTon;
    int geschwindigkeit;
}Auto;

void Auto_init(Auto *this, int anzahlRaeder, char* hupTon);
void Auto_fahre(Auto *this);
void Auto_hupe(Auto *this, int anzahl);

oder dieser..

typedef struct
{
    Auto* pAuto;
}Garage;

void Garage_init(Garage *this);
void Garage_init2(Garage *this, Auto *pAuto);
void Garage_einparken(Garage *this, Auto *pAuto);
void Garage_ausparken(Garage *this);

Zitat
Zitat
Um auch noch mehr Senf loszuwerden, könnte ich noch anbieten, dass man in C nichtsdestotrotz auch objektorientiert programmieren kann – die Frage ist dann nur, ob man das will, mit Funktionspointern für Methoden, ohne impliziten this-Pointer und ohne so aparte¹ Sachen wie Konstrukturaufrufen mit new und Destruktoraufrufen mit delete.
Präprozessor to the rescue?  :-D
Eben das wäre mir zu umständlich. Dann doch lieber C++
Das war nicht ernstgemeint. Ich würde den Präprozessor für diese Sachen nicht benutzen. Impliziter this-Pointer, new und delete sind Syntaxdetails. Mein C-Code muss nicht wie C++-Code aussehen. Ich kann den this-Pointer einfach explizit mitgeben und direkt Konstruktor und Destruktor statt new aufrufen.
Nun aber genau bietet C++ eben den Komfort von impliziten this-Pointern um die man sich nicht kümmern muss bzw.
der automatisierte Aufruf des Destructors bei lokalen Objekten die man in C "von Hand" zerstören müsste, oder?

Zitat
Warum benützen wir nicht ganz normal das Wort fancy? Wir sagen doch auch "Manager" und nicht "Leiter" ^
Tun wir das? Du gehörst wohl auch zu den Leuten, die Laptop statt Klapprechner sagen? ;)
Du hast mich erwischt :D Klapprechner ist auch gut ^^

kevin

  • Administrator
  • Beiträge: 2 767
    • Profil anzeigen
Gespeichert
« Antwort #8 am: 17. February 2012, 13:29 »
Wo es allerdings in Sachen "so tun, als gäbe es einen Zusammenhang" (was auch immer du damit meinst) einen Unterschied zwischen C und C++ geben sollte, kann ich im Moment nicht nachvollziehen.
Damit ist Code wie dieser gemeint ^^
[...]
Okay, und wo wird da jetzt ein nicht vorhandener Zusammenhang vorgetäuscht, was außerdem auch noch in C++ nicht der Fall wäre?

Zitat
Nun aber genau bietet C++ eben den Komfort von impliziten this-Pointern um die man sich nicht kümmern muss bzw.
der automatisierte Aufruf des Destructors bei lokalen Objekten die man in C "von Hand" zerstören müsste, oder?
Implizite this-Pointer sind ein Komfortfeature, genau. Ob ich sie jetzt hinschreibe oder nicht macht meine Entwicklung nicht wesentlich einfacher oder schwieriger. Über solche Sachen muss ich mir keine großen Gedanken machen, und damit sie sind nebensächlich.

Dass Destruktoren von Hand aufgerufen werden müssen, ist m.E. kein Bug, sondern ein Feature. Damit sieht man im Code genau den Ablauf von oben nach unten und es passiert nicht noch implizit irgendwelche Magie, die man übersehen könnte, oder von der man wissen müsste, in welcher Reihenfolge sie ausgeführt wird.
Thou shalt not follow the NULL pointer, for chaos and madness await thee at its end.

DerHartmut

  • Beiträge: 236
    • Profil anzeigen
    • Mein Blog
Gespeichert
« Antwort #9 am: 17. February 2012, 13:53 »
Um ehrlich zu sein ist dieser ganze fetzige (meine deutsche Interpretation von "fancy") Hype um "Objektorientierung" eh nicht von allzulange Dauer - es wurde bewiesen, dass objektorientierte Programme inperformanter und fehleranfählliger sind und dazu neigen zu organisiert zu sein.

Für die Betriebssystementwicklung würde ich eher Assembler benutzen - C ist viel zu langsam und der gcc erzeugt sehr schlechten Assembler-Code, ein Mensch kann hier weitaus besser per Hand optimieren und alles (und damit meine ich wirklich alles) aus der Hardware herausholen was nur irgendwie geht. Auch die sehr komplizierte und fehleranfällige Syntax von C (die sich bis nach C++ durchzieht und dort durch die Objektorientierung noch komplizierter und fehleranfälliger wurde) machen es - gepaart mit dem schlechten Assembler-Code, der produziert wird - meiner Meinung nach nicht sehr empfehlenswert.

Wenn du aber dennoch Objektorientierung willst (wie gesagt, ich rate davon ab - macht das System nur unnötig komplex und überladen) solltest du dann schon C nehmen (ist halt nicht ganz so komplex wie C++) und den daraus produzierten Code per Hand nachoptimieren und ggf. wenn du über die Kenntnisse verfügst, ein Assembler-Programm schreiben, welches diese Optimierung übernimmt.
$_="krJhruaesrltre c a cnp,ohet";$_.=$1,print$2while s/(..)(.)//;
Nutze die Macht, nutze Perl ;-)

Jidder

  • Administrator
  • Beiträge: 1 625
    • Profil anzeigen
Gespeichert
« Antwort #10 am: 17. February 2012, 14:29 »
Vorsicht vor dem Troll! Und DemHartmut ist auch nicht zu trauen.
Dieser Text wird unter jedem Beitrag angezeigt.

Paprikachu

  • Beiträge: 6
    • Profil anzeigen
Gespeichert
« Antwort #11 am: 17. February 2012, 15:16 »
Dass Destruktoren von Hand aufgerufen werden müssen, ist m.E. kein Bug, sondern ein Feature. Damit sieht man im Code genau den Ablauf von oben nach unten und es passiert nicht noch implizit irgendwelche Magie, die man übersehen könnte, oder von der man wissen müsste, in welcher Reihenfolge sie ausgeführt wird.
C hat ein Problem (nicht): Exceptions. Durch Exceptions entsteht das Problem, dass Routinen zur Freigabe von Resourcen beim Verlassen des Scopes durch eine solche nicht aufgerufen werden. Dieses Problem ist mit RAII in C++ sehr elegangt gelöst.
Das kleinere Problem, welches übrigens auch noch in C vorhanden ist, ist, dass man auf den Aufruf einer Freigabe-Funktion vergessen könnte. Auch das kann in C++ mit RAII nicht passieren.

Was du mit der Magie-Aussage sagen willst, verstehe ich nicht. Da gibt es weder was zu übersehen, noch interessiert da die Reihenfolge. (Die im C++-Standard übrigens exakt festgelegt ist.)

kevin

  • Administrator
  • Beiträge: 2 767
    • Profil anzeigen
Gespeichert
« Antwort #12 am: 17. February 2012, 15:25 »
Ich glaube nicht, dass Exceptions wirklich viel ändern. In C hast du stattdessen eben ein goto dastehen, aber gemeinsam haben beide Methoden, dass du irgendwo mittendrin rausspringst. In C hat man halt am Ende irgendein fail:-Label. Genauso kann man in Sprachen mit Exceptions am Funktionsende die Exception fangen, Zeug freigeben und weiterwerfen. In manchen Sprachen wie C++ wäre das syntaktisch nicht so schön, aber das liegt nicht am Konzept.

Was die Reihenfolge angeht, kann ich mir gut vorstellen, dass das definiert ist, aber kennt diese Definition auch jeder?
Thou shalt not follow the NULL pointer, for chaos and madness await thee at its end.

Paprikachu

  • Beiträge: 6
    • Profil anzeigen
Gespeichert
« Antwort #13 am: 17. February 2012, 15:48 »
Ich glaube nicht, dass Exceptions wirklich viel ändern. In C hast du stattdessen eben ein goto dastehen, aber gemeinsam haben beide Methoden, dass du irgendwo mittendrin rausspringst. In C hat man halt am Ende irgendein fail:-Label.
Der Unterschied ist: Mit Exceptions muss ich keinen extra Code für die Fehlerbehandlung in Funktionen schreiben, die den Fehler gar nicht behandeln.
Außerdem: Warum willst du einem Objekt explizit sagen müssen, dass es sich aufräumen soll? Wenn es zerstört wird (Sei es automatisch durch verlassen des Scopes, oder durch eine Deallokation über free()/delete), ist die einzig logische Konsequenz, dass Resourcen mitaufgeräumt werden. Warum das also nicht automatisieren?

Genauso kann man in Sprachen mit Exceptions am Funktionsende die Exception fangen, Zeug freigeben und weiterwerfen. In manchen Sprachen wie C++ wäre das syntaktisch nicht so schön, aber das liegt nicht am Konzept.
Au ja. Das führt dann zu tollen try/catch/finally-Konstruktionen. Ein kleines Beispiel in Java:
try
{
    mystream.read();
}

finally
{
    try { mystream.close(); } catch(IOException e) {}
}
Oder das ganze nochmal auf C++ übertragen (Wir nehmen für einen kurzen Moment an, es gäbe kein RAII):
// Exceptions wurden vorher am Stream eingeschaltet

try
{
    char c;
    mystream.read(&c, sizeof c);
}

catch(...)
{
    mystream.close();
    throw;
}
Willst du echt sowas schreiben müssen?

Was die Reihenfolge angeht, kann ich mir gut vorstellen, dass das definiert ist, aber kennt diese Definition auch jeder?
Die Reihenfolge ist für den Benutzer irrelephant. Aber ich kann dich beruhigen: Die Reihenfolge ist so intuitiv, wie man sich nur denken kann: Umgekehrt zur Reihenfolge der Erzeugung. Und spätestens, wenn man Variablen hat, die voneinander abhängen (Zeiger/Referenzen aufeinander halten), kann das nur die einzige Möglichkeit sein.
Und wenn dich das immer noch nicht überzeugt: Du musst auch lernen, wie man Variablen benutzt, wie man Funktionen schreibt und dass der Returnwert der Main-Funktion nicht void ist. Sowas gehört zu den Sprachgrundlagen.
« Letzte Änderung: 17. February 2012, 15:55 von Paprikachu »

kevin

  • Administrator
  • Beiträge: 2 767
    • Profil anzeigen
Gespeichert
« Antwort #14 am: 17. February 2012, 16:23 »
Der Unterschied ist: Mit Exceptions muss ich keinen extra Code für die Fehlerbehandlung in Funktionen schreiben, die den Fehler gar nicht behandeln.
Außerdem: Warum willst du einem Objekt explizit sagen müssen, dass es sich aufräumen soll? Wenn es zerstört wird (Sei es automatisch durch verlassen des Scopes, oder durch eine Deallokation über free()/delete), ist die einzig logische Konsequenz, dass Resourcen mitaufgeräumt werden. Warum das also nicht automatisieren?
Um im Code genau zu sehen, wann was passiert. Meinetwegen kannst du das in einer hypothetischen Sprache sogar so machen, dass es einen Compilerfehler gibt, wenn ich das Objekt nicht aufräume, aber ich will, dass explizit dasteht, was gemacht wird.

Zitat
Au ja. Das führt dann zu tollen try/catch/finally-Konstruktionen. Ein kleines Beispiel in Java:
Wie gesagt, die Syntax in C++ ist nicht so toll dafür. Zum Beispiel mit Ada sähe das so aus (schnell aus dem Internet zusammenkopiert, weil ich es lang nicht mehr benutzt habe und mit Dateien wohl eh nie was gemacht habe):
procedure foo is
    file: File_Type;
    c: Character;
begin
    Open(File => file, Name => "/usr/local/widgets/data", Mode => In_File);
    Get (file, c);
    Close(file);
exception
    when Error: others =>
        Close(file);
end foo;

Das kriegt man sich auch nochmal etwas netter formatiert, aber grundsätzlich bist du da syntaktisch schon wieder recht nahe an der expliziten, aber trotzdem übersichtlichen C-Version (da ist halt ein zusätzliches if drin, was auch wieder den möglichen Ablauf genauer klarmacht, aber das könnte man in der hypothetischen Sprache sicher auch noch ein bisschen Syntactic Sugar verpassen):
int foo(void)
{
    FILE* f = fopen(...);
    ret = do_something(f);
    if (ret < 0) {
        goto fail;
    }

    ret = 0;
fail:
    fclose(f);
    return ret;
}

Zitat
Die Reihenfolge ist für den Benutzer irrelephant. Aber ich kann dich beruhigen: Die Reihenfolge ist so intuitiv, wie man sich nur denken kann: Umgekehrt zur Reihenfolge der Erzeugung. Und spätestens, wenn man Variablen hat, die voneinander abhängen (Zeiger/Referenzen aufeinander halten), kann das nur die einzige Möglichkeit sein.
Und Zeiger/Referenzen können nur im Konstruktor gesetzt werden und nicht irgendwann in der Mitte der Lebenszeit des Objekts? Für mich klingt das nach einem Rezept für subtile Fehler.

Zitat
Und wenn dich das immer noch nicht überzeugt: Du musst auch lernen, wie man Variablen benutzt, wie man Funktionen schreibt und dass der Returnwert der Main-Funktion nicht void ist. Sowas gehört zu den Sprachgrundlagen.
Ich muss es lernen, wenn ich diese Sprache benutzen will. Aber ich muss es nicht gut finden und ich kann beschließen, dass ich die Sprache deswegen nicht benutzen will (wenn es der einzige Grund wäre, wäre das vermutlich ein eher schwaches Argument, aber es trägt dazu bei).
Thou shalt not follow the NULL pointer, for chaos and madness await thee at its end.

Sannaj

  • Beiträge: 103
    • Profil anzeigen
Gespeichert
« Antwort #15 am: 17. February 2012, 16:25 »
Man sollte sich schon zwischen C und C++ entscheiden. C nehmen und dann die Objektorientierung per CPP versuchen nachzuäffen halte ich für wenig ratsam.

Für den Kernel würde ich persönlich C nehmen.
C++ bietet zwar die OOP, der wichtigste Hebel für die Verwendung der OOP, nämlich die GUI-Elemente, sowie die meisten anderen Vorteile der OOP (Dateizugriff, Agenten, etc.) fallen im Lowlevel Bereich weg. (OOP ist immer dann stark, wenn man viele Dingen hat, die sich nur in Details oder nur in Parametern unterscheiden, und auf die verschiedene Aktionen angewand werden können. Mir fallen da in Bezug auf Betriebssysteme eigentlich nur Tasks ein.) Im Lowlevelsektor fallen mir da spontan nur Darüber hinaus, passieren bei der OOP sehr viele Dinge auch implizit und nicht unbedingt vorhersehbar, was bei der Lowlevel Programmierung nicht unbedingt von Vorteil ist.

Ein weiterer Schwachpunkt von C ist das Fehlen von überladbaren Methoden, das bei der Lowlevelprogrammierung aber auch nicht so zu Geltung kommt. (Der Kernel ist ja keine Mathesoftware, die generische sin(), cos() etc. Funktionen braucht.). Auch dynamische Strukturen sind im Kernel recht unbrauchbar. Präprozessormagie zum lösen des Überladeproblems ist übrigens nicht implementierungsunabhängig, oder extrem aufwendig: http://carolina.mff.cuni.cz/~trmac/blog/2005/the-ugliest-c-feature-tgmathh/

Paprikachu

  • Beiträge: 6
    • Profil anzeigen
Gespeichert
« Antwort #16 am: 17. February 2012, 17:06 »
Um im Code genau zu sehen, wann was passiert. [...], aber ich will, dass explizit dasteht, was gemacht wird.
Das verstehe ich nicht. Warum willst du das wissen? Wie genau sich das Objekt aufräumt, ist ein Implementierungsdetail, und dass es aufgeräumt wird, garantiert dir der Standard. Wozu willst du das sehen/hinschreiben müssen? Für mich ist das etwas logisch schon implizites, eine Selbstverständlichkeit. Ich würde mich wundern, das hinschreiben zu müssen. Offenbar ist diese Logik noch nicht zu anderen "modernen" OOP-Sprachen durchgedrungen.

Meinetwegen kannst du das in einer hypothetischen Sprache sogar so machen, dass es einen Compilerfehler gibt, wenn ich das Objekt nicht aufräume
Bei Stackobjekten: Okay. Aber wie willst du das bei Objekten am Heap bzw Zeigern auf Objekte lösen? Der Compiler kann nicht wissen, welche Lebenszeit das Ding haben soll. Es kann sogar vorkommen, dass du selbst es nicht weißt, weil es z.B. von äußeren Umständen, wie Spielern auf einem Server abhängt.

Wie gesagt, die Syntax in C++ ist nicht so toll dafür. Zum Beispiel mit Ada sähe das so aus (schnell aus dem Internet zusammenkopiert, weil ich es lang nicht mehr benutzt habe und mit Dateien wohl eh nie was gemacht habe):
[...]
Okay, das ist immerhin schon besser. Aber ich bezweifle, dass diese Fehlerbehandlung in Ada so mächtig ist, wie ein try in anderen Sprachen (Ich kann Ada nicht.). Kann ich damit z.B. nur Fehler in den Zeilen X und Y fangen? Trotzdem: Auch hier kann ich das Freigeben vergessen.

Das kriegt man sich auch nochmal etwas netter formatiert, aber grundsätzlich bist du da syntaktisch schon wieder recht nahe an der expliziten, aber trotzdem übersichtlichen C-Version (da ist halt ein zusätzliches if drin, was auch wieder den möglichen Ablauf genauer klarmacht, aber das könnte man in der hypothetischen Sprache sicher auch noch ein bisschen Syntactic Sugar verpassen):
[...]
Da hier nur eine Resource im Spiel ist, ist alles noch relativ einfach. Sobald es aber mehrere werden, gibts für jede Überprüfung ein if, nur damit der richtige Returncode zurückgegeben wird. Exceptions sind um Welten eleganter, einfacherer und sicherer zu verwenden. (Vorausgesetzt, man hat RAII.)

Und Zeiger/Referenzen können nur im Konstruktor gesetzt werden und nicht irgendwann in der Mitte der Lebenszeit des Objekts? Für mich klingt das nach einem Rezept für subtile Fehler.
Was du mir damit sagen willst, verstehe ich nicht. Es ist ganz natürlich, dass Objekte voneinander abhängen. Indem man die Objekte umgekehrt zu ihrer Erzeugungsreihenfolge zerstört, kann eben nichts schief gehen.

Ich muss es lernen, wenn ich diese Sprache benutzen will.
Jeder Sprache hat ihre Eigenheiten. Außerdem ist das nicht wirklich eine Regel, die viel Lernen bedarf. Meiner Meinung nach ist das eines der logischsten Dinge überhaupt, die man eigentlich gar nicht nachsehen muss.

kevin

  • Administrator
  • Beiträge: 2 767
    • Profil anzeigen
Gespeichert
« Antwort #17 am: 17. February 2012, 17:09 »
C++ bietet zwar die OOP, der wichtigste Hebel für die Verwendung der OOP, nämlich die GUI-Elemente, sowie die meisten anderen Vorteile der OOP (Dateizugriff, Agenten, etc.) fallen im Lowlevel Bereich weg. (OOP ist immer dann stark, wenn man viele Dingen hat, die sich nur in Details oder nur in Parametern unterscheiden, und auf die verschiedene Aktionen angewand werden können. Mir fallen da in Bezug auf Betriebssysteme eigentlich nur Tasks ein.) Im Lowlevelsektor fallen mir da spontan nur Darüber hinaus, passieren bei der OOP sehr viele Dinge auch implizit und nicht unbedingt vorhersehbar, was bei der Lowlevel Programmierung nicht unbedingt von Vorteil ist.
Es gibt in einem OS schon recht viele Sachen, wo es sinnvoll ist, objektorientiert ranzugehen. Aber sie brauchen in der Regel keine größere Magie von der Programmiersprache, sondern oft z.B. nur unterschiedliche Implementierungen eines Interfaces. Manchmal noch ein bisschen Vererbung, aber meistens nicht.

Dateien sind zum Beispiel eindeutig ein Objekt. Ihre Implementierung unterscheidet sich je nachdem, ob sie auf ext2, FAT oder in einer Ramdisk liegen. Aber um das umzusetzen brauche ich nur pro FS-Treiber ein const struct von Funktionspointern und keine großartige C++-Magie.

Zitat
Ein weiterer Schwachpunkt von C ist das Fehlen von überladbaren Methoden, das bei der Lowlevelprogrammierung aber auch nicht so zu Geltung kommt. (Der Kernel ist ja keine Mathesoftware, die generische sin(), cos() etc. Funktionen braucht.).
Mit _Generic geht das wohl in C11 so einigermaßen. Obwohl ich jetzt einfach mal sage, einen Schönheitswettbewerb gewinnt das nicht.

Womit auch das Stichwort für das eine wirklich fehlende Feature in C gegeben ist: Richtige Generics. Also der Teil von Templates, den man wirklich braucht. Auf das Berechnung von Primzahlen zur Compilezeit verzichte ich gern. ;)
Thou shalt not follow the NULL pointer, for chaos and madness await thee at its end.

Paprikachu

  • Beiträge: 6
    • Profil anzeigen
Gespeichert
« Antwort #18 am: 17. February 2012, 17:16 »
Womit auch das Stichwort für das eine wirklich fehlende Feature in C gegeben ist: Richtige Generics. Also der Teil von Templates, den man wirklich braucht. Auf das Berechnung von Primzahlen zur Compilezeit verzichte ich gern. ;)
Nur weil du sowas mit diesen Features machen kannst, heißt das nicht, dass diese schlecht sind. Im Übrigen hat C++11 dafür jetzt sowieso ein neues Sprachfeature, constexpr.

kevin

  • Administrator
  • Beiträge: 2 767
    • Profil anzeigen
Gespeichert
« Antwort #19 am: 17. February 2012, 17:39 »
Das verstehe ich nicht. Warum willst du das wissen? Wie genau sich das Objekt aufräumt, ist ein Implementierungsdetail, und dass es aufgeräumt wird, garantiert dir der Standard. Wozu willst du das sehen/hinschreiben müssen? Für mich ist das etwas logisch schon implizites, eine Selbstverständlichkeit. Ich würde mich wundern, das hinschreiben zu müssen. Offenbar ist diese Logik noch nicht zu anderen "modernen" OOP-Sprachen durchgedrungen.
Damit ich daran erinnert werde, dass hier was passiert. Dass ich auf einen Blick die möglichen Codepfade sehen kann. Ohne den Code zu kennen und ohne zu wissen, welche anderen Klassen Destruktoren haben.

Zitat
Bei Stackobjekten: Okay. Aber wie willst du das bei Objekten am Heap bzw Zeigern auf Objekte lösen? Der Compiler kann nicht wissen, welche Lebenszeit das Ding haben soll. Es kann sogar vorkommen, dass du selbst es nicht weißt, weil es z.B. von äußeren Umständen, wie Spielern auf einem Server abhängt.
Das löse ich so wie in C++: Für alles, was auf dem Heap ist, wird keine Freigabe erzwungen.

Zitat
Okay, das ist immerhin schon besser. Aber ich bezweifle, dass diese Fehlerbehandlung in Ada so mächtig ist, wie ein try in anderen Sprachen (Ich kann Ada nicht.). Kann ich damit z.B. nur Fehler in den Zeilen X und Y fangen? Trotzdem: Auch hier kann ich das Freigeben vergessen.
Naja, du kannst überall einen begin-end-Block anfangen, damit geht es dann wieder stark in Richtugn eines try-catch.

Und ja, ich gebe ja zu, dass vergessen lassen nicht gut ist. Aber das bekämpft man am besten nicht mit automagischem Fixen, sondern mit Compilerfehlern, wenn man es vergisst.

Zitat
Da hier nur eine Resource im Spiel ist, ist alles noch relativ einfach. Sobald es aber mehrere werden, gibts für jede Überprüfung ein if, nur damit der richtige Returncode zurückgegeben wird. Exceptions sind um Welten eleganter, einfacherer und sicherer zu verwenden. (Vorausgesetzt, man hat RAII.)
Wenn im Lauf der Funkion mehrere Sachen angelegt werden, die freigegeben werden müssen, gibt es mehrere verschiedene Labels, ja. So what?

Zitat
Und Zeiger/Referenzen können nur im Konstruktor gesetzt werden und nicht irgendwann in der Mitte der Lebenszeit des Objekts? Für mich klingt das nach einem Rezept für subtile Fehler.
Was du mir damit sagen willst, verstehe ich nicht. Es ist ganz natürlich, dass Objekte voneinander abhängen. Indem man die Objekte umgekehrt zu ihrer Erzeugungsreihenfolge zerstört, kann eben nichts schief gehen.
Hab mal ein bisschen damit rumgespielt (verzeih mir mein C++, ich würde das normal nicht tun):
#include <iostream>

using namespace std;

class Foo {
    public:
        int x;
        Foo* other_foo;
        Foo* nochn_foo;

        Foo(int x);
        ~Foo();
};

Foo::Foo(int x) : x(x), other_foo(0), nochn_foo(0)
{
}

Foo::~Foo()
{
    cout << x << endl;

    if (other_foo != NULL) {
        cout << "other_foo: " << other_foo->nochn_foo->x << endl;
    }

    if (nochn_foo) {
        delete nochn_foo;
    }
}

int main(void)
{
    Foo foo(2);
    Foo bar(42);

    bar.nochn_foo = new Foo(1337);
    foo.other_foo = &bar;

    return 0;
}

Das gleiche Ziel kann man wahrscheinlich auch einfacher erreichen, aber dass dieses Muster so mal in einem komplexeren Programm auftritt halte ich für gar nicht unwahrscheinlich. Ist natürlich Mist, wie valgrind richtig erkennt:
==16813== Invalid read of size 4
==16813==    at 0x4009D7: Foo::~Foo() (in /tmp/c/a.out)
==16813==    by 0x400A9D: main (in /tmp/c/a.out)
==16813==  Address 0x4c3b040 is 0 bytes inside a block of size 24 free'd
==16813==    at 0x4A0528C: operator delete(void*) (vg_replace_malloc.c:387)
==16813==    by 0x400A28: Foo::~Foo() (in /tmp/c/a.out)
==16813==    by 0x400A91: main (in /tmp/c/a.out)
Thou shalt not follow the NULL pointer, for chaos and madness await thee at its end.

 

Einloggen